fix(security): 代码审查安全加固 — 三批次修复(6H/13M/12L)
第一批(快速修复): - H6: main.py --debug 默认值 True→False,防止 Werkzeug RCE - M2+M3: /search 增加 validate_query + top_k 范围限制(1-50) - M4: context_count 范围限制(0-10) + 异常捕获 - L3: assert → raise RuntimeError(生产环境 API Key 检查) - H1: SSE 错误事件移除 traceback 字段 第二批(安全加固): - H2+H3: 文档接口路径遍历 realpath 校验 + 文件类型/大小限制 - H4+H5: 批量上传文件大小检查 - M6: LIKE 查询通配符转义 - M1: 37 处 str(e) 异常信息统一脱敏(6 文件) - M5: CORS 生产环境限制来源 - M7: SESSION_MANAGER None 保护(503) - M11: subprocess 参数注入防护(白名单 + -- 分隔符) 第三批(架构改进): - M8+M9: 提取 JSON 解析共享工具(extract_json_object/list) - M10: Prompt 注入检测防御(prompt_guard.py) - M12: 解析器文件大小限制(Excel 50MB/TXT 20MB/PDF 100MB) - M13: 全局单例竞态条件双重检查锁定(engine/bm25/intent_analyzer)
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
@@ -24,7 +23,7 @@ from functools import wraps
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
# 导入 LLM 工具函数
|
||||
from core.llm_utils import call_llm
|
||||
from core.llm_utils import call_llm, extract_json_object
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -378,47 +377,12 @@ class AnswerGrader:
|
||||
return result
|
||||
|
||||
def _extract_json(self, response: str) -> dict:
|
||||
"""
|
||||
多策略从 LLM 响应中提取 JSON 对象
|
||||
|
||||
策略优先级:
|
||||
1. markdown 代码块提取 ```json ... ```
|
||||
2. 直接 json.loads
|
||||
3. 正则匹配第一个 {...} 块
|
||||
"""
|
||||
"""多策略从 LLM 响应中提取 JSON 对象(使用共享工具)"""
|
||||
result = extract_json_object(response)
|
||||
if result is not None:
|
||||
return result
|
||||
if not response:
|
||||
raise ValueError("LLM 返回为空")
|
||||
|
||||
# 策略1:提取 markdown 代码块
|
||||
json_match = re.search(r'```(?:json)?\s*([\s\S]*?)\s*```', response)
|
||||
if json_match:
|
||||
json_str = json_match.group(1).strip()
|
||||
try:
|
||||
result = json.loads(json_str)
|
||||
if isinstance(result, dict):
|
||||
return result
|
||||
except json.JSONDecodeError:
|
||||
pass # 继续下一策略
|
||||
|
||||
# 策略2:直接解析整个响应
|
||||
try:
|
||||
result = json.loads(response.strip())
|
||||
if isinstance(result, dict):
|
||||
return result
|
||||
except json.JSONDecodeError:
|
||||
pass # 继续下一策略
|
||||
|
||||
# 策略3:正则匹配最外层 JSON 对象
|
||||
brace_match = re.search(r'\{[\s\S]*\}', response)
|
||||
if brace_match:
|
||||
try:
|
||||
result = json.loads(brace_match.group(0))
|
||||
if isinstance(result, dict):
|
||||
return result
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 全部策略失败
|
||||
raise json.JSONDecodeError(
|
||||
f"无法从 LLM 响应中提取有效 JSON,响应前300字: {response[:300]}",
|
||||
response, 0
|
||||
|
||||
Reference in New Issue
Block a user