FinBuddy 安全修复与分层重构

· FinBuddy

今日进展

  1. 修复 P0 安全风险:SQL 注入防护、LLM 生成 SQL 安全校验、裸 except 吞异常修复(23 处)
  2. 修复 P1 分层违规:从 routes 层抽取业务逻辑到 services,routes 只做参数校验和调度
  3. 中间件增强:新增 execute_raw_query()、get_market_db_path()、get_all_custom_experts() 等方法

关键代码/伪代码

SQL 注入防护 — 白名单校验

# market_db.py: 新增 _validate_identifier 白名单校验
# 只允许字母、数字、下划线,防止 SQL 注入
# 背景:LLM 可能生成包含特殊字符的表名/列名,
#       如果直接拼接到 SQL 中,就有注入风险

FUNCTION _validate_identifier(name: str) -> str:
    # 正则:必须以字母或下划线开头,后跟字母/数字/下划线
    # 拒绝:空格、分号、引号、注释符等 SQL 注入字符
    IF NOT re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', name):
        RAISE ValueError(f"非法标识符: {name}")
    RETURN name

# 使用:所有动态表名/列名必须经过校验
# 修复前(危险):query = f"SELECT * FROM {table_name}"
# 修复后(安全):
query = f"SELECT * FROM {_validate_identifier(table_name)}"

分层重构 — routes 提取到 services

# 重构前: routes/simulation.py 包含 510+ 行业务逻辑
# 问题:routes 层既做参数校验又做业务逻辑,职责不清
# 重构后: 业务逻辑抽取到 services/simulation/simulation_service.py

# routes/simulation.py (重构后 — 只做参数校验和调度)
@router.post("/simulation/run")
ASYNC def run_simulation(request: SimulationRequest):
    validated = validate_request(request)     # 参数校验
    result = await simulation_service.run(validated)  # 委托 service
    RETURN ApiResponse(data=result)

# services/simulation/simulation_service.py (新增 — 业务逻辑)
CLASS SimulationService:
    ASYNC DEF run(self, params) -> SimulationResult:
        # Step1: 构建回测场景(股票、时间范围、初始资金)
        scenario = self._build_scenario(params)
        # Step2: 创建 Backtrader 引擎并执行
        engine = BacktraderEngine(scenario)
        result = await engine.run()
        # Step3: 格式化结果(必须包含回测免责声明)
        RETURN self._format_result(result)

中间件统一访问

# 重构前: 各模块直接 import db 路径和实例
# 问题:硬编码路径散落各处,改数据库位置要改 N 个文件
# 重构后: 统一通过中间件访问,改一处全站生效

# data_manager.py 改为:
# 修复前: db_path = "/hardcoded/path/to/finbuddy_market.db"
# 修复后:
db_path = get_middleware().get_market_db_path()

# market.py 改为:
# 修复前: middleware = MarketMiddleware(db_path)  # 每次新建实例
# 修复后:
middleware = get_middleware()  # 单例,全局唯一

# llm_parser.py 改为:
# 修复前: experts = 直接查数据库获取专家列表
# 修复后:
experts = get_middleware().get_all_custom_experts()

遇到的问题

  • SVN 服务器上 SVN 1.7.14 版本太老,中文 commit message 显示乱码(GBK 编码问题),需要本地转码才能看到完整提交说明
  • 23 处裸 except 分布在 8 个文件中,逐个排查耗时较长,最后用 grep 批量定位
  • simulation_service 从 routes 抽取时,依赖关系较复杂(引用了 4 个 middleware 方法),需要确保中间件方法先就位

明日计划

  • 继续 services 目录按业务域拆分:agent/backtest/knowledge/market/report/system/watchlist
  • middleware 目录按业务域拆分:business/market/news/tdx/watch
  • 新增合规违规文件清单文档