From 6deba8fae293954aabc93556ac5ec4af66f5e668 Mon Sep 17 00:00:00 2001 From: lacerate551 <128470311+lacerate551@users.noreply.github.com> Date: Fri, 5 Jun 2026 15:26:38 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5=E6=8A=A5=E5=91=8A=202026-06-05?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/代码审查报告_2026-06-05.md | 180 ++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 docs/代码审查报告_2026-06-05.md diff --git a/docs/代码审查报告_2026-06-05.md b/docs/代码审查报告_2026-06-05.md new file mode 100644 index 0000000..34a4e37 --- /dev/null +++ b/docs/代码审查报告_2026-06-05.md @@ -0,0 +1,180 @@ +## 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` 等敏感文件。