8.5 KiB
RAG-Agent 代码审查报告(精简版)
审查日期:2026-06-05
排除说明:storage 模块尚未启用(暂不纳入);用户认证/权限由后端服务负责(生产环境 RAG 服务为无状态接口,不做鉴权)。
一、高危问题(6 项)
H1. SSE 错误事件泄露完整堆栈信息
- 文件:
api/chat_routes.py:1955 /rag接口 SSE 生成器在异常时将traceback.format_exc()完整堆栈直接发给客户端,暴露调用栈、文件路径、代码行号、内部变量。- 修复:移除 traceback 字段,仅在服务端日志记录,客户端返回通用错误消息。
H2. 文档更新/删除接口存在路径遍历风险
- 文件:
api/document_routes.py:652,714 update_document和delete_document直接将 URL 中的doc_path拼接到文件路径,未做安全校验。可构造../../路径遍历载荷。- 修复:使用
os.path.realpath()解析最终路径,验证是否在 DOCUMENTS_PATH 目录下。
H3. serve_document_file 路径遍历风险
- 文件:
api/document_routes.py:139 - 文件服务接口同样存在路径遍历风险。虽然有 DEV_MODE 开关,但默认值为
'true'。 - 修复:添加 realpath 校验。
H4. 文档更新接口缺少文件类型和大小校验
- 文件:
api/document_routes.py:621 update_document(PUT) 未验证文件类型和大小,直接file.save(filepath)。与之对比,upload_document有完整校验。- 修复:添加与 upload 一致的 ALLOWED_EXTENSIONS 和 MAX_FILE_SIZE 校验。
H5. 批量上传接口缺少文件大小校验
- 文件:
api/document_routes.py:350 batch_upload_documents对每个文件只检查了扩展名,未检查文件大小。可批量上传超大文件导致磁盘耗尽。- 修复:在循环内添加 MAX_FILE_SIZE 校验。
H6. main.py debug 模式默认开启 + 监听 0.0.0.0
- 文件:
main.py:29-31 --debug默认True,--host默认0.0.0.0。Flask 调试模式启用 Werkzeug 交互式 debugger,可通过触发异常执行任意代码。- 修复:
--debug默认值改为False。
二、中危问题(13 项)
M1. 多处异常响应直接暴露内部错误信息
- 文件:
document_routes.py:332,467,733;kb_routes.py:523,636;feedback_routes.py:98,116;sync_routes.py:119;audit_routes.py:100等。 - 大量
except块直接str(e)返回给客户端,可能包含数据库路径、SQL 片段、文件系统结构。 - 修复:统一使用通用错误消息,原始异常仅记录到服务端日志。
M2. /search 接口缺少输入安全验证
- 文件:
api/chat_routes.py:1974 - 未调用
validate_query()做注入检测和长度限制,与/chat、/rag不一致。 - 修复:添加
validate_query(query)调用。
M3. /search 的 top_k 参数未校验范围
- 文件:
api/chat_routes.py:1989 - 可传
top_k=999999导致内存溢出。 - 修复:
top_k = max(1, min(int(top_k), 50))。
M4. context_count 参数未校验范围
- 文件:
api/document_routes.py:821 - 未限制范围且非整数字符串会 ValueError 导致 500。
- 修复:try/except +
max(0, min(n, 10))。
M5. CORS 配置允许所有来源
- 文件:
api/__init__.py:70 CORS(app)默认允许*跨域。生产环境应限制为已知前端域名。- 修复:根据 APP_ENV 条件配置 origins。
M6. LIKE 通配符注入风险
- 文件:
api/kb_routes.py:503 kb_name含%或_时会导致非预期的 LIKE 匹配行为。- 修复:对 LIKE 特殊字符转义后再拼入模式。
M7. SESSION_MANAGER 为 None 时未处理
- 文件:
api/session_routes.py:35,64,83,102 - 初始化失败时 SESSION_MANAGER 为 None,调用方法会触发 AttributeError 导致 500。
- 修复:使用前检查 None,返回 503。
M8. LLM 调用缺少统一的超时和重试机制
- 文件:
core/llm_utils.py - 部分 LLM 调用无超时控制,长时间阻塞会耗尽 worker。
@retry装饰器只在部分方法上使用。 - 修复:在
_call_llm层面统一超时和重试。
M9. LLM 输出 JSON 解析不够健壮
- 文件:
core/agentic.py、core/agentic_answer.py、core/agentic_quality.py等 - 多处 LLM 返回的 JSON 解析缺少多策略提取和重试,仅靠 prompt 约束。exam_pkg 已修复但 core 模块尚未统一。
- 修复:提取 exam_pkg 的
_extract_json为公共工具,core 模块统一使用。
M10. Prompt 注入风险
- 文件:
core/engine.py:2017、core/agentic_answer.py:83 - 用户输入直接拼入 prompt,未做净化。恶意输入可操控 LLM 输出。
- 修复:对用户输入做基本的 prompt 注入检测(如检测 "ignore previous instructions" 等模式)。
M11. subprocess.run 命令参数注入风险
- 文件:
parsers/mineru_parser.py:632 - file_path 中特殊字符(如以
-开头的文件名)可能被命令行工具解释为选项。 - 修复:在文件路径前插入
--分隔符;对 backend、lang 参数做白名单校验。
M12. Excel/文本解析器无文件大小限制
- 文件:
parsers/excel_parser.py:81、parsers/txt_parser.py:15 - 一次性加载全文件到内存,超大文件导致 OOM。
- 修复:解析前检查文件大小,设定上限(如 50MB)。
M13. 全局变量缓存竞态条件
- 文件:
api/document_routes.py:100、api/kb_routes.py:46 - 模块级全局变量
_kb_manager等在多线程 gunicorn 下存在竞态。 - 修复:使用
threading.Lock保护或改用flask.current_app.config。
三、低危问题(12 项)
L1. config.py 硬编码第三方 API 端点 xiaomimimo.com 作为默认值(第 20 行)— 改为空字符串,要求环境变量显式配置。
L2. python-dotenv 未安装时静默跳过,服务可能 fail-open 启动(config.py:11)— 生产环境缺失时抛异常。
L3. assert 校验可被 python -O 跳过(api/__init__.py:236)— 改为 raise ValueError。
L4. /chat 的 history 未限长度(chat_routes.py:1247)— 可消耗大量 token。
L5. history 元素结构未验证(chat_routes.py:1117)— 缺少字段时 KeyError 导致 500。
L6. safe_filename 运算符优先级不明确(document_routes.py:94)— 加括号明确。
L7. DocStore glob 模式未转义特殊字符(document_routes.py:267)— 用 glob.escape()。
L8. 相对路径 .data/images 因工作目录不同可能解析错误(chat_routes.py:59)— 改用 PROJECT_ROOT 绝对路径。
L9. asyncio.run() 在 Flask 请求上下文中兼容性问题(chat_routes.py:1744)。
L10. Excel 同一文件被重复读取多次(excel_parser.py:81,89)— 应复用 ExcelFile 对象。
L11. PDF 图片提取 doc 对象异常时未关闭(image_extractor.py:78)— 改用 with 语句。
L12. TXT 解析器异常用 print 而非 logger(txt_parser.py:26)。
四、修复优先级
按修复成本从低到高排序:
第一批:快速修复(半天,改几行代码)
| 编号 | 问题 | 改动量 |
|---|---|---|
| H6 | main.py debug 默认开启 | 1 行 |
| M3 | /search top_k 范围校验 | 2 行 |
| M2 | /search 加 validate_query | 2 行 |
| M4 | context_count 范围校验 | 3 行 |
| L3 | assert 改 raise | 3 行 |
| H1 | SSE 移除 traceback 字段 | 5 行 |
第二批:安全加固(1-2 天)
| 编号 | 问题 | 改动量 |
|---|---|---|
| H2+H3 | 文档接口路径遍历 realpath 校验 | ~30 行 |
| H4+H5 | 文档更新/批量上传加文件校验 | ~30 行 |
| M6 | LIKE 通配符转义 | ~10 行 |
| M1 | 异常信息统一脱敏 | 多文件,每处 2-3 行 |
| M5 | CORS 生产环境限制来源 | ~5 行 |
| M7 | SESSION_MANAGER None 保护 | ~10 行 |
| M11 | subprocess 参数注入防护 | ~5 行 |
第三批:架构改进(1-2 周)
| 编号 | 问题 | 说明 |
|---|---|---|
| M8+M9 | LLM 调用统一超时/重试/解析 | 提取 exam_pkg 经验为公共工具 |
| M10 | Prompt 注入防御 | 需设计检测规则 |
| M13 | 全局变量竞态修复 | threading.Lock |
| M12 | 解析器文件大小限制 | 统一加前置校验 |
五、做得好的方面
SQL 查询全部使用参数化查询,无注入风险;validate_query() 对聊天输入做了注入检测和违禁词过滤;safe_filename 对上传文件做了基本防护;filter_response() 能过滤 API 密钥等敏感信息;exam_pkg 的输入校验体系完整(已在本轮开发中加固);.gitignore 正确排除了 .env 等敏感文件。