首页Home 产品Products 服务Services 案例Work 知识库Blog 关于About 联系Contact
KNOWLEDGE · 2026-08

AI Agent 批量修复开源项目漏洞:35 个 bug 的实战复盘

AI-agent bug hunting at scale: a 35-bug post-mortem

#AI Agent #安全审计 #FastAPI #测试驱动

背景

一个开源项目发布安全赏金:找到 bug,提交 PR 附可复现测试,按有效 bug 数结算。 目标仓库是一个 FastAPI 写的记忆系统(Python 3.10+)。我们采用 「3 个并行审计 Agent + 主代理人工复核」的策略,最终发现并修复 35 个确定性 bug。 这篇复盘记录方法论,可直接复制到类似任务。

策略:并行 Agent 分片扫描

把仓库按模块域切成三片,每片交给一个独立审计 Agent:

  • Agent A:核心服务层(session / memory 读写 / 导出 / 摘要聚合)
  • Agent B:CLI 命令与配置(参数校验、路径处理、错误码)
  • Agent C:集成层(MCP / LangGraph / UI 路由 / legacy 代码)

每份任务书明确要求:只报告「能写失败测试」的确定性 bug,并给出 文件 + 行号 + 代码片段 + 为什么是 bug + 最小复现思路。 这样把模糊的「看看有没有问题」变成可验证的交付物。

筛选:从候选到确定

三个 Agent 共报告约 17 个候选。我们不是直接照单全收,而是按三条标准筛:

  • 确定性:能否写出稳定复现的失败测试?不能=不报。
  • 是否已报过:同类 bug 前几轮已修的不重复(query 注入、分页截断、路径穿越等)。
  • 影响面:静默数据丢失 > 崩溃 > 体验问题。

最终锁定 7 个高价值 bug,全部先写红牌测试确认失败,再修复转绿。

最有价值的几类 bug

复盘时我们给 bug 按「真实危害」排了序,这里挑三类典型:

1. 截断方向错误:丢了最新的消息

对话记忆提取函数从最旧的消息开始累加字符,超过上限就 break—— 结果超长对话里最新消息(本次要记的内容)全被丢弃, 喂给 LLM 的 query 反而是旧消息。极端情况单条消息超限直接返回空串报 400。 正确做法:从尾部保留最新,必要时截断头部。

2. 归档时间错位:今天的记录永远消失

session 摘要文件按「记忆的 created_at」归档,而 daily summary 按「今天」聚合。 导入一条旧数据(created_at 是过去),它的摘要被写进历史文件,今天的日报永远读不到—— 静默数据丢失。修复:按写入时间归档。

3. 假成功:CLI 报告 OK 但 token 已过期

--hours 0 生成「立即过期」的会话,CLI 却打印激活成功。 参数校验缺失(服务层无 >0 检查)。修复:服务层 fail-closed + CLI 层 min=1。

协作教训:一个 bounty 一个 PR

我们最初把不同轮次的 bug 拆成多个 PR 提交,维护者批量关闭,明确要求 「一个 bounty 一个 PR,多个 bug 放同一个 PR」。 整改:把所有轮次的修复合并进唯一存活的 PR,强推更新,冲突逐个解决。 这是赏金任务最容易踩的坑——先读清规则再动手。

可复制的审计检查单

这份清单从实战中提炼,适用于任何 Python/FastAPI 项目的首轮审计:

  • 时间处理:naive/aware datetime 混用、时区错位、归档日期与聚合日期不一致
  • 截断方向:保留的是旧的还是新的?超限时是丢数据还是显式失败?
  • 异常吞噬:except Exception 后是否静默返回「成功」?有没有假成功路径?
  • 参数校验:负数/零/超长/None 是否有 fail-closed 检查?
  • 边界循环:for 循环迭代 dict 的 key 而不是 values?列表为空时的行为?
  • 返回契约:API 返回 envelope dict,调用方是否按裸列表迭代?

结果

35 个 bug 全部附带「失败测试→修复→全量回归通过」的证据链合并提交。 全程没有任何代码在本地之外执行,所有验证都在真实环境跑通。 这套流程的价值在于:AI Agent 负责扫描与初筛,人类(或主代理)负责 验证与决策——各司其职,速度与可靠性兼得。

给 Agent 的任务书,要具体到「能写失败测试」才算合格;给维护者的 PR, 要具体到「红→绿证据链」才算专业。