让 agent 做安全审计,最容易出的问题不是漏报,是它太擅长「找到」漏洞——一串看起来很像漏洞的清单,每条都带着 CVSS 分数,认真读下去发现一半是「缺少最佳实践」。
这个技能最反直觉的地方,是它花了很大力气阻止自己下结论。
| 项目 | 内容 |
|---|---|
| 类型 | Agent Skill(20 个文件,约 310KB 文档与校验器) |
| 仓库 | cloudflare/security-audit-skill |
| 技术栈 | Markdown 提示词 + 零依赖 Node 校验脚本(.cjs) |
| 许可 | MIT |
| 热度 | 54.8k star / 3.6k fork / 104 open issue(2026-10-31 快照) |
| 状态 | 2026-06-18 开源,最后一次提交 2026-09-14 |
它跑的是什么
六个阶段,每个阶段都有明确产物,不是「让模型自由发挥」:
| 阶段 | 做什么 | 产出 |
|---|---|---|
| 1 勘察 | 摸清架构、信任边界、输入面、历史证据、确定性覆盖 | architecture.md、coverage-ledger.json |
| 2 覆盖驱动的狩猎 | 从账本单元分配独立的 hunter,记录检查项,用 coverage critic 找缺口 | 结构化候选 |
| 3 候选验证 | 每个候选交给全新的 verifier,任务是证伪 | 裁决结果 |
| 4 结构化输出 | 写 confirmed / needs_validation / rejected 三类记录 | findings.json |
| 5 独立记录复核 | 新 agent 复核最终的源码主张;材料性替换要再来一个 | 修正后的记录 |
| 6 中性报告 | 从已验证的记录和账本推导报告 | REPORT.md、FINDINGS-DETAIL.md、NEEDS-VALIDATION.md |
两个校验器贯穿全程:建完账本、以及之后每次更新账本,父进程都要跑 validate-coverage-ledger.cjs;Phase 4 和每次 Phase 5 替换后跑 validate-findings.cjs。都是零依赖的 .cjs,自带测试。
设计一:把「我审完了」变成可校验的断言
大多数 agent 交付物的软肋是那句「已完成」。这个技能的处理方式是把覆盖率做成一份机器可校验的账本:
- 勘察阶段就建初始账本,列出有哪些单元要审;
- 每次账本更新后必须通过校验器,格式不对、覆盖声明不闭合就过不去;
- 多轮审计是叠加的:拿上一轮的账本定位缺口、重验改动过的源码、把带过来的证据标成「当前源码」而不是「已完成」。
这条设计解决的是一个很隐蔽的失败:模型审了 30% 就说审完了,而你没有任何依据反驳它。有了账本,你可以问「第 41 号单元为什么没有记录」。
设计二:查的 agent 不能是找的 agent
三条原则连起来看,是整个技能的骨架:
对抗式验证。 检查一个发现的 agent,永远不是发现它的那个 agent。
候选不是「再想想对不对」,而是交给一个全新的 verifier 去反驳。Phase 5 再来一轮:新 agent 独立复核最终的源码主张,如果记录被材料性替换,还要再找一个验证者。
裁决只有三档,而且只有 confirmed 有严重级别:
| 裁决 | 含义 |
|---|---|
confirmed | 完整的源码链路 + 有界的观测结果 |
needs_validation | 有确切的未解决事实,没有 severity |
rejected | 被证伪的候选 |
needs_validation 这一档是整份文档里最聪明的设计。它给了模型一个合法的地方说「我卡在这里,缺的是这个事实」——而不是逼它挑一个高分贝的形容词把空白填满。
定级锚点也写得很硬,最有用的是一句判别式:
如果你说不出具体的损害,严重度就比你感觉的要低。
设计三:没有沙箱,就不执行
安全工具最容易造成的伤害,是它自己在验证时造成的。所以「通用执行安全」那一节写得像操作系统手册:
目标代码(构建、测试、进程、浏览器、模拟器、fuzzer、fixture)只能在 OS 级沙箱里跑,且必须同时满足:
- 无外网;需要本地 client/server 通信时只开隔离的 loopback 命名空间;
- 空环境,从显式白名单注入安全值,
HOME、临时目录、缓存都是 scratch 局部; - 目标与工具链只读,目标控制的进程只能写自己分配到的
scratch/; - CPU、内存、进程数、文件大小、磁盘、墙钟都有明确限额。
少一条就不执行。 把缺失的沙箱能力记成 needs_validation 的阻塞项,并给一个安全的验证计划。
同时明确禁止:探测已部署端点、碰共享基础设施、用生产身份、拿别人的数据、对着线上进程测可用性、发布产物、花付费配额。
工具自己也按「被攻击对象」设计
这部分我读得最仔细,因为它解释了一个问题:为什么一个只读源码的审计工具,需要这么复杂的写入隔离?
答案是:目标代码是不可信的。它可以在 scratch/ 里放一个符号链接指向 ~/.ssh,等你「正常地」复制产物时把私钥带出去。
所以产物提升是 11 步的:父进程先持有 scratch/ 和 artifacts/ 的不可继承目录描述符(永不下发给 agent 或沙箱);提升时逐级用 no-follow 的目录相对操作走,绝不按路径重新打开;叶子文件 fstat 必须「普通文件 + 链接数恰好为 1 + 在字节限额内」;复制后重做 fstat,身份、类型、链接数、大小任何一项变了就丢;不递归复制、不 glob、不解压、不提升符号链接/FIFO/socket/设备/目录/硬链接。
连 agent ID 的规则都考虑到了 Windows 设备名(con、prn、aux、com1…)和大小写折叠碰撞。
这是我在 agent 工具里见过最像样的「不信任自己的输出目录」设计。多数技能根本不会想到这一层。
十条反模式
SKILL.md 末尾列了十条,几乎每一条都能对应到某个真实存在的坏审计报告:
- 把「偏离清单」当成漏洞
- 给防御纵深提建议,但拿不出可达的越界
- 在真实或共享环境里测试
- 猜 provider、proxy、浏览器、身份或部署行为
- 把「同类主体本来就有这个权限」或「自影响」当成跨边界结果
- 报告的解析器/运行时效果比实际观测到的更强
- 只写散文的 hunter 结果——既无法去重,也无法验证
- 把上一轮的同源结论当范例,锚定这一轮的狩猎
- 给
needs_validation定严重级别 - 在独立验证之前就写报告,或者让散文和 JSON 互相矛盾
第 7 条和第 10 条尤其值得抄到别的工具里:结构化输出不是为了好看,是为了能被别人否定。
上手
npx skills add https://github.com/cloudflare/security-audit-skill --skill security-audit然后在你想审的代码库里说人话就行:
security audit this codebase
find security vulnerabilities in ./src
do a security review, output to ~/audits/my-project触发词匹配(security audit、find vulnerabilities、pen-test the code 等)后自动激活。直接说「审计这个代码库」走完整模式;问安全问题或做聚焦的漏洞工作走引导模式,不给报告产物。
完整模式的输出目录默认 ~/security-audit-skill/<repo-name>/run-<N>,只有你显式指定、且父进程确认该目录被版本控制忽略时,才会写进目标仓库。
要求三条:支持工具调用和并行子 agent 的模型、Node(跑校验器)、一个 OS 级沙箱。
坑
1. 沙箱门槛卡住了大半能力
这是最现实的限制。「没有完整沙箱就不执行目标代码」是对的,但大多数人手上没有这样的环境。结果是:源码审得动,动态验证全都停在 needs_validation——报告的结论会明显偏保守。
要发挥它的完整能力,得先把沙箱搭起来(容器 + 无网络 + 只读挂载 + 资源限额 + scratch 目录)。这件事本身就是个工程活。
2. 跑一次不等于审完
README 里有一句诚实到不利于营销的话:测试中,单轮大约只能找到重复多轮总发现的一半。 也就是说「用了一次,没发现问题」几乎不构成任何结论。
3. 两个月没有新提交
6 月开源,最后一次提交 9 月 14 日,104 个 issue 挂着。它是 Cloudflare 那套「fleet-wide 漏洞发现 harness」演化之前的单仓库雏形——博客里那套更复杂,这个只是起点。
4. 它审的是「代码里的边界失败」
不替代渗透测试、不替代合规审计、审不了部署配置和人的流程。文档自己也反复交回这类判断:需要部署侧的事实就用 needs_validation 交还给你。
5. 产物很长
六个产物文件,FINDINGS-DETAIL.md 可能很长。它假设读者会认真看,而不是扫一眼结论。
适合谁
| 适合 | 不适合 |
|---|---|
| 有人愿意读完整报告的安全评审 | 想要「一键出一份合规报告」 |
| 有现成容器/沙箱环境,能跑动态验证 | 只能在裸机上跑,碰不得 Docker |
| 想给 agent 立一套「什么样的发现才配叫漏洞」的标准 | 只想要一个扫描器 |
| 代码库是自研的、源码可读 | 依赖大量闭源服务、部署配置不在仓库里 |
一点判断
这个技能真正的贡献不是「用 AI 找漏洞」——那件事大家都在做——而是它把「不知道」制度化了。
needs_validation 是一个很奇怪的东西:它既不是漏洞也不是误报,它是一句「有一个确切的缺口,我缺这个事实才能判断」。多数 AI 工具没有这个档位,所以模型只有两个选择:要么报一个夸大的漏洞,要么沉默。前者污染报告,后者骗过自己。
配套的三个机制都指向同一件事:别让自己相信自己。查的人不能是找的人;只有 confirmed 配得上严重级别;覆盖率要能对着账本被质问。
还能抄走的一条是**「防御纵深缺口不是漏洞」**。如果 A 层已经挡住了攻击,B 层缺失只是加固建议——这句话能砍掉安全报告里至少三分之一的注水内容。
要泼的冷水也很直接:沙箱门槛、单轮只覆盖一半、两个月没更新。它现在是一份好的方法论和一个能跑的实现,不是一个安全保证。真要交付审计结论,人和流程仍然是最后一环。
链接
- 仓库 · SKILL.md
- Cloudflare 博客:Build your own vulnerability harness
- 反馈信箱:
security-ai-research@cloudflare.com
