数据库拆分设计
/database
main.db ← 用户、管理员、应用列表
java.db ← Java 漏洞、CVE、POC、EXP
mysql.db ← MySQL 漏洞、CVE、POC、EXP
redis.db
nginx.db
每个应用独立 SQLite 的优点:
- 某应用数据损坏不影响整个系统
- 可单独备份或迁移到 PostgreSQL
- 漏洞数据量大时不会拖慢其他应用的查询
main.db 表结构
-- 用户表
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
password TEXT, -- bcrypt hash
role TEXT, -- superadmin / admin / user
status INTEGER, -- 0=禁用 1=正常
created_at DATETIME
);
-- 应用注册表
CREATE TABLE applications (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT, -- "java"
db_name TEXT, -- "java.db"
db_path TEXT, -- "/database/java.db"
created_at DATETIME
);
应用数据库(每个应用独立)
CREATE TABLE vulnerabilities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
description TEXT,
severity TEXT, -- critical/high/medium/low
affected_version TEXT,
fixed_version TEXT,
created_at DATETIME
);
CREATE TABLE cves (
id INTEGER PRIMARY KEY AUTOINCREMENT,
cve_id TEXT, -- CVE-2024-12345
cvss REAL,
description TEXT
);
CREATE TABLE pocs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
file_path TEXT, -- /uploads/java/uuid.py
description TEXT
);
CREATE TABLE exps (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
file_path TEXT,
description TEXT
);
-- 多对多关联
CREATE TABLE vulnerability_pocs (vuln_id INT, poc_id INT);
CREATE TABLE vulnerability_exps (vuln_id INT, exp_id INT);
CREATE TABLE vulnerability_cves (vuln_id INT, cve_id INT);
动态创建应用数据库
添加应用时,后端自动创建 SQLite 文件并初始化表:
// NestJS service
async createApplication(name: string) {
const dbPath = path.join("database", `${name}.db`);
const db = new Database(dbPath);
// 初始化表结构
db.exec(`
CREATE TABLE IF NOT EXISTS vulnerabilities (...);
CREATE TABLE IF NOT EXISTS cves (...);
CREATE TABLE IF NOT EXISTS pocs (...);
CREATE TABLE IF NOT EXISTS exps (...);
`);
db.close();
// 记录到 main.db
await this.mainDb.run(
"INSERT INTO applications (name, db_name, db_path) VALUES (?, ?, ?)",
[name, `${name}.db`, dbPath]
);
}
RBAC 三角色权限
| 角色 | 权限 |
|---|---|
| superadmin | 全部权限,管理用户和管理员 |
| admin | 管理漏洞/CVE/POC/EXP,不能添加管理员 |
| user | 只能查询漏洞和下载 POC/EXP |
JWT 鉴权:
// NestJS Guard
@Injectable()
export class RolesGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const { user } = context.switchToHttp().getRequest();
const requiredRoles = this.reflector.get<string[]>("roles", context.getHandler());
return requiredRoles.includes(user.role);
}
}
文件安全
POC/EXP 文件禁止通过静态目录暴露:
❌ GET /uploads/java/poc.py (直接静态访问)
✅ GET /api/download/poc/:id (JWT 鉴权后返回文件流)
@Get("download/poc/:id")
@UseGuards(JwtAuthGuard)
async downloadPoc(@Param("id") id: string, @Res() res: Response) {
const poc = await this.pocService.findById(id);
res.download(poc.file_path, poc.name);
}
上传文件名用 UUID 避免路径遍历:
const filename = `${uuidv4()}${path.extname(file.originalname)}`;
版本匹配建议
不要简单字符串比对,用 semver 范围查询:
import semver from "semver";
function isVulnerable(version: string, affected: string): boolean {
// affected: ">=8.0.0 <8.0.31"
return semver.satisfies(version, affected);
}
"8.0.9" < "8.0.31" 用字符串比较会得到错误结果(字典序 9 > 3),必须用 semver 数字化比较。
推荐技术栈
| 层 | 推荐 |
|---|---|
| 后端框架 | NestJS 或 Next.js App Router |
| ORM | Prisma(SQLite 支持好,类型安全) |
| 鉴权 | JWT + bcrypt |
| 前端 | Vue3 + Element Plus 或 shadcn/ui |
| 部署 | PM2 + Nginx 反代 |
