Files
rag/api/audit_routes.py
lacerate551 90b915232a 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)
2026-06-05 15:26:32 +08:00

135 lines
4.1 KiB
Python
Raw 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.
"""
审计日志 API
路由:
- GET /audit/logs - 查询审计日志(管理员)
"""
from flask import Blueprint, request, jsonify
import logging
logger = logging.getLogger(__name__)
from auth.gateway import require_gateway_auth
from data.db import get_connection
audit_bp = Blueprint('audit', __name__)
@audit_bp.route('/audit/logs', methods=['GET'])
@require_gateway_auth
def get_audit_logs():
"""
查询审计日志
参数:
- limit: 返回条数默认50
- days: 查询天数默认7
- action: 操作类型过滤(可选)
返回:
{
"logs": [
{
"id": 1,
"user_id": "admin001",
"username": "admin",
"action": "rag_query",
"query": "xxx",
"result_summary": "...",
"role": "admin",
"department": "管理部",
"ip_address": "127.0.0.1",
"duration_ms": 1234,
"timestamp": "2025-01-01 12:00:00"
}
],
"total": 100
}
"""
limit = request.args.get('limit', 50, type=int)
days = request.args.get('days', 7, type=int)
action_filter = request.args.get('action', '')
try:
with get_connection("session") as conn:
# 构建查询
where_clauses = ["created_at >= datetime('now', ?)"]
params = [f'-{days} days']
if action_filter:
where_clauses.append("action = ?")
params.append(action_filter)
where_sql = " AND ".join(where_clauses)
# 查询总数
count_sql = f"SELECT COUNT(*) FROM audit_logs WHERE {where_sql}"
total = conn.execute(count_sql, params).fetchone()[0]
# 查询日志
query_sql = f"""
SELECT id, user_id, username, action, query, result_summary,
role, department, ip_address, duration_ms, created_at
FROM audit_logs
WHERE {where_sql}
ORDER BY created_at DESC
LIMIT ?
"""
params.append(limit)
rows = conn.execute(query_sql, params).fetchall()
logs = []
for row in rows:
logs.append({
"id": row[0],
"user_id": row[1],
"username": row[2],
"action": row[3],
"query": row[4],
"result_summary": row[5],
"role": row[6],
"department": row[7],
"ip_address": row[8],
"duration_ms": row[9],
"timestamp": row[10]
})
return jsonify({"logs": logs, "total": total})
except Exception as e:
logger.error(f"审计查询异常: {e}")
return jsonify({"error": "查询失败", "logs": [], "total": 0}), 500
def log_audit_event(user_id: str, username: str, action: str,
query: str = None, result_summary: str = None,
role: str = None, department: str = None,
ip_address: str = None, duration_ms: int = None):
"""
记录审计日志(供其他模块调用)
Args:
user_id: 用户ID
username: 用户名
action: 操作类型rag_query, chat, feedback, sync 等)
query: 查询内容
result_summary: 结果摘要
role: 用户角色
department: 部门
ip_address: IP地址
duration_ms: 耗时(毫秒)
"""
try:
with get_connection("session") as conn:
conn.execute("""
INSERT INTO audit_logs
(user_id, username, action, query, result_summary,
role, department, ip_address, duration_ms)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (user_id, username, action, query, result_summary,
role, department, ip_address, duration_ms))
except Exception as e:
# 审计日志写入失败不应影响主流程
logger.debug(f"审计日志写入失败: {e}")
pass