Files
rag/docs/代码审查报告_2026-06-05.md
2026-06-05 15:26:38 +08:00

181 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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` 等敏感文件。