审查日期: 2026-06-21
审查范围: 安全漏洞、代码质量、依赖安全、配置问题、架构问题
文件数量: 18 个 Python 源文件
- 文件:
api.py第 221 行 - 描述:
file_path = UPLOAD_DIR / file.filename直接使用用户上传的文件名拼接路径,攻击者可通过../../etc/cron.d/evil等路径写入任意位置。 - 修复建议: 使用
Path(file.filename).name取纯文件名,或使用werkzeug.utils.secure_filename()清洗文件名,同时校验文件扩展名白名单。
- 文件:
api.py第 38 行,src/web/app.py第 31 行 - 描述: 两个 FastAPI 应用均设置
allow_origins=["*"],且无任何认证机制,所有图谱数据可被任意网页读取和修改。 - 修复建议: 限制 CORS 来源,添加 API Key 或 JWT 认证。
- 文件:
api.py全部路由,src/web/app.py全部路由 - 描述: 所有 API 端点(创建/删除实体、清空图谱、导入数据等)完全开放,任何人都可以修改或删除数据。
- 修复建议: 至少添加 API Key 中间件或 Basic Auth,关键操作(删除、清空)需要额外确认。
- 文件:
api.py第 507-514 行 - 描述:
GET /api/settings/llm返回 API Key 的前6位和后4位(key[:6] + "****" + key[-4:]),对于短密钥可能泄露足够多的信息。且该接口无认证保护。 - 修复建议: 完全隐藏 API Key,只返回是否已配置(
"configured": true/false)。
- 文件:
api.py第 417-444 行 - 描述: 用户输入的
question直接传递给 LLM 进行查询,无任何输入清洗或长度限制,存在提示注入(Prompt Injection)攻击风险。 - 修复建议: 限制输入长度,对用户输入进行基本清洗,LLM 响应不应直接执行任何操作。
- 文件:
main.py第 19 行 - 描述:
uvicorn.run("api:app", ..., reload=True)在生产环境使用热重载有安全和性能风险。 - 修复建议: 仅在开发模式启用
reload。
- 文件:
config.py第 33 行 - 描述: 默认服务器配置
"host": "0.0.0.0"会暴露到所有网络接口。 - 修复建议: 默认改为
"127.0.0.1"。
- 文件:
graph_algorithms.py第 117、159、240、289、294 行 - 描述: 多处使用
except:而非except Exception:或具体异常类型,会吞掉KeyboardInterrupt、SystemExit等不应被捕获的异常。 - 修复建议: 所有
except:改为except Exception:,并记录日志。
- 文件:
database.py(578行) vsgraph_store.py(515行) - 描述: 两个模块都实现了 SQLite 存储层,
database.py用于api.py,graph_store.py用于src/web/app.py,导致 schema 不完全一致(database.py有updated_at列,graph_store.py没有),维护困难。 - 修复建议: 统一为一个数据库模块,
api.py和src/web/app.py共用。
- 文件:
database.py第 578 行:db = Database() - 描述: 全局
db实例在多线程(FastAPI + 后台处理线程)中共享,_conn()上下文管理器每次创建新连接但没有连接池或线程本地存储,并发写入可能冲突。 - 修复建议: 使用
threading.local()管理连接,或引入连接池。
- 文件:
config.py第 26 行 - 描述: API Key 以明文 JSON 存储在
data/config.json,文件权限未限制。 - 修复建议: 文件权限设为
0o600,或支持环境变量覆盖(LLM_API_KEY)。
- 文件:
api.py第 216-223 行 - 描述:
upload_document端点直接将整个文件读入内存并写入磁盘,无大小限制,可能被用于 DoS 攻击。 - 修复建议: 添加文件大小限制(如 10MB),使用 FastAPI 的
UploadFile配合流式读取。
- 文件:
api.py第 75 行:properties: dict = {} - 描述: 虽然 Pydantic 会深拷贝默认值,但使用
Field(default_factory=dict)是更明确的做法。 - 修复建议: 改为
properties: dict = Field(default_factory=dict)。
- 文件:
api.py第 151-164 行 - 描述: 后台 LLM 抽取线程中的异常仅写入数据库任务记录,不会出现在日志中,难以排查问题。
- 修复建议: 在
except块中添加logger.error(...)调用。
- 文件:
api.py第 66-67 行 (TextIngestRequest), 第 96-99 行 (QueryRequest) - 描述:
text和question字段无长度限制,超长输入可能耗尽 LLM API 额度或导致内存问题。 - 修复建议: 使用
Field(max_length=50000)等约束。
- 文件:
graph_algorithms.py第 115-118 行 - 描述: PageRank 计算失败时静默返回空列表,不记录失败原因。
- 修复建议: 添加
logger.warning("PageRank 计算失败", exc_info=True)。
- 文件:
entity_extractor.py第 148-176 行 - 描述:
_extract_by_regex中所有pattern_name分支都返回EntityType.OTHER,switch 逻辑冗余。 - 修复建议: 简化为直接使用
EntityType.OTHER,无需条件分支。
- 文件:
models.py第 61 行:properties: dict = {}, 第 66 行:source_doc_ids: list[str] = [] - 描述: Pydantic 会处理,但使用
Field(default_factory=...)更规范。 - 修复建议: 使用
Field(default_factory=dict)和Field(default_factory=list)。
- 文件:
document_parser.py第 26-57 行 - 描述:
parse()方法接受任意文件路径,未校验文件类型是否在白名单内。 - 修复建议: 添加允许的扩展名白名单校验。
| 维度 | 评分 (满分10) | 说明 |
|---|---|---|
| 安全 | 3/10 | 路径穿越、无认证、API Key 泄露、CORS 全开、提示注入风险 |
| 质量 | 6/10 | 代码功能完整,但裸 except 多处、异常处理不完善、缺少输入验证 |
| 架构 | 5/10 | 两套并行数据库层导致维护混乱,API 层重复,全局单例无并发保护 |