← 返回博客
Ai预计阅读 11 分钟

Cloudflare 开源了自用的审计器:值钱的是它会反驳

发布于 2026年10月28日
Cloudflare 开源了自用的审计器:值钱的是它会反驳

Cloudflare 这周发布了一个 skill,把编码 agent 变成安全审计员,随后它就成了 GitHub 周榜上涨最快的仓库——七天大约 16,400 颗 star,总数 51,000。官方自己的说法异常克制:这只是内部漏洞挖掘流水线演化出来的单仓库起点,公开出来是为了让别人也能搭一套自己的。

值得读的不是那份攻击类型目录,虽然它确实很全。值得读的是关于结论如何被辩倒的那部分。

六个阶段,其中一个只是记账

这套流程是结构化而非对话式的,这是它跟“让 Claude 找 bug”最先分开的地方。

第一阶段是侦察:画出架构、信任边界、输入面,还有——这一点不太常见——把它们写进 architecture.md 和 coverage-ledger.json。第二阶段按账本把彼此隔离的猎手分配到各个单元,记录每个人查了什么,然后跑覆盖度评审,找出没人看过的地方。第三阶段把每个独立候选交给一个全新的验证者,其唯一职责是证伪它。第四阶段把 confirmed、needs_validation 或 rejected 记录写进 findings.json,并按 JSON schema 校验。第五阶段由全新 agent 核验最终的源码结论,任何实质性替换都要再过一位独立验证者。第六阶段从已验证的记录推导出报告。

两个校验器是零依赖的纯 Node 脚本:validate-coverage-ledger.cjs 在账本创建后以及每次更新后运行,validate-findings.cjs 在第四阶段运行,并在第五阶段每次替换后再跑一次。它们是仓库里最无趣的文件,也是其余部分能站得住的原因。一个产不出符合 schema 的 finding 的 agent,就不能声称自己找到了什么。

暗色墙面上层层叠叠的便利贴与一张六格流程图,两个格子之间拉着红线,左下一盏台灯投出狭窄的暖光

三种结论才是真正的设计

多数 agent 安全工具只有两种输出:一串 bug,或者什么都没有。这个工具有三种,而中间那一档才是思考所在。

confirmed 意味着完整的源码链路加上一个有边界、被观测到的结果。needs_validation 意味着一个确切未解决的事实,且不指派严重级别。rejected 意味着一个被主动证伪的候选,它被记录下来,而不是删掉。

第三种很少见,而我认为是对的。一个被证伪的候选是关于这套代码的证据——它告诉下一轮不要在那里浪费时间,也告诉评审者当初考虑过什么。多数工具把这份东西直接丢掉。

底层的设计原则在 README 里说得很直白:只确认已经成立的边界失效;如果一条有源码依据的线索被卡住,就让它停在 needs_validation,附上那个确切未解决的事实,而不是把它升格成 finding。还有一条相关规则:纵深防御的缺口不算漏洞——如果 A 层挡住了攻击,B 层的缺失只是一条加固建议。

读过机器生成的漏洞报告的人,都很清楚这些规则为什么存在。LLM 安全评审的失效模式不是漏掉 bug,而是自信的噪音:看着像漏洞的模式,被标成高危,白白耗掉一个 triage 团队的一周。

对抗式验证,字面意义上的

最关键的一句话:检查 finding 的 agent,永远不是发现它的那个 agent。

这不是提示词里的一句叮嘱,而是编排层面的约束,也是这类工具能产出任何可信结果的唯一办法。你问一个 LLM“这个 finding 对吗”,它对自己的输出一定会同意。给它一个全新的上下文、另一个 agent 实例,以及明确证伪的任务,你得到的才更接近一次代码评审。

第五阶段把这个又推了一步:全新 agent 核验最终的源码结论,如果某个结论被实质性替换,再由另一位独立验证者检查替换后的版本。猎手和最终留存下来的记录之间,隔着两层。

那个诚实的数字

设计原则里埋着这么一句:在 Cloudflare 的测试里,单次运行大约能找到反复多次运行合计发现的漏洞的一半。

公开这一点是一种选择。它告诉你这是一台覆盖率机器,而不是神谕,也重设了工作流——不是在发版前跑一次,而是反复跑,让账本慢慢累积。仓库明确支持这种用法:对同一仓库的多次运行是叠加的,用之前的账本和 findings 去定位缺口,重新验证改动过的源码,并沿用当前源码的证据,而不是把过期的工作当成已覆盖。

一半对于人类审计师的首轮工作也是个合理估计。这让人安心还是让人绝望,取决于你怎么看这个行当。

会挡掉大多数人的那条要求

README 里列了一条硬要求,多数用户会略过它,然后一头撞上:为构建、测试、进程、浏览器、模拟器、fuzzer 和 fixture 提供操作系统级强制沙箱,禁用外部网络,使用清洗过的白名单环境,强制资源上限,写入只允许落在临时路径。

没有这些控制,工作流会把线索停在 needs_validation,而不会去执行目标代码。这个判断是对的——毕竟你是在拿语言模型的建议去跑一套代码库——但这也意味着,任何没有容器基础设施的人,只能把它当成一场“静态分析加推理”的练习。

这也意味着它不是笔记本上的工具,尽管安装行写的是 npx。安装就一条命令:

npx skills add https://github.com/cloudflare/security-audit-skill \
  --skill security-audit

用法就是开口问一句:

security audit this codebase

这里的反差很怪:调用是一句话,前置条件是一整套沙箱策略。两者之间的落差,就是大部分部署工作量要去填的地方。

输出到底长什么样

三份产物,都是推导出来的,不是手写的:REPORT.md、FINDINGS-DETAIL.md 和 NEEDS-VALIDATION.md,再加上底层的 findings.json 与覆盖账本。

这个拆分比听起来有用。单一漏洞报告会把每个发现压进同一个语域,于是你会看到一条高危 SQL 注入和一条缺失的 CSP 响应头并排出现。把已确认的发现、悬而未决的问题和推导出的摘要分开,意味着读 NEEDS-VALIDATION.md 的人清楚自己看的是进行中的工作,而不是结果。

默认输出目录是 ~/security-audit-skill/<repo-name>/run-<N>,只有当你显式指向一个被 gitignore 的目录时,工作流才会写进目标仓库内部。细节很小,直觉很对:仓库里的审计产物会被误提交,而一份已经提交的未修复漏洞清单,本身就是一次事故。

怎么判断它到底有没有用

跑两遍。这是 README 暗示了却没明说的测试:因为运行是叠加的、单次只找到大约一半,所以对同一个 commit 的第二次运行,就是这套工具在你代码库上覆盖率的直接测量。如果第二次没跑出新东西,要么你确实是干净的,要么它的猎杀类别跟你的技术栈对不上。

然后去看 rejected 日志。一次运行确认了五个问题、否掉了四十个候选,说明它在干活。一次运行确认了四十个、一个都没否掉,说明它已经停止验证了,你得先手动把那四十个读一遍,再决定信不信。

它对这一品类意味着什么

三点,简短说。

Agent skill 正在变成一种分发形态。这不是一个你 import 的库,而是一个 markdown 文件夹加两个校验脚本,由模型在运行时读取。仓库是二十来个文件的指令,按目标类型切分的攻击类别目录——内存安全与二进制、LLM 与 agent 面、Web 协议与认证、客户端、供应链、云与部署、RPC 与消息、资源耗尽、数据隔离、桌面与本地 IPC。这个结构本身就是产品。

验证正在成为差异化所在。现在每个厂商都能生成一条看着很像的 finding。但几乎没有谁能告诉你,哪些经过了对抗式审查还站得住、写进了 schema,而且被否掉的也留着。

而“诚实数字”的习惯正在扩散。一个敢公开“我们只找到一半”的厂商,它下一句声明更可信。

仓库是 MIT 许可,这意味着有意思的问题不是它能不能用,而是你的团队是否负担得起它正常运转所需要的那个沙箱。

相关文章