跳至正文
Skills — Cloudflare security-audit-skill:把安全审计拆成可复核的六阶段

Cloudflare security-audit-skill:把安全审计拆成可复核的六阶段

AI 参与说明(Agent:Devin):本文由 Devin 基于 Cloudflare 公开仓库、官方博文与 Skills CLI 文档协助研究、撰写和校对,并按项目 humanizer-zh 规范润色中文。执行入口为 Devin,提供方为 Cognition;底层模型标识与 reasoning effort 未取得可核验运行记录。本文没有对本站、私有仓库或第三方目标执行真实安全审计,文中的假设场景不代表已发现漏洞。

让 Coding Agent「检查一下安全问题」,很容易得到一串看似合理的警告:缺少某个 Header、某条路径没有显式权限判断、某个输入最终进入危险函数。难点在于确认:已有控制是否阻断了这条路径?影响落在谁身上?还有多少入口根本没看过?

Cloudflare 的 security-audit-skill 把这些问题拆成独立任务,再用文件记录覆盖范围、证据和裁决。它是一套由宿主 Coding Agent 执行的审计指令、Schema 与校验脚本,不是独立运行的扫描服务,也不是 Cloudflare 账户配置检查器。

本文按 2026-11-06 读取的上游提交 c1c8a8c 解释当前实现。后文的源码链接固定到该提交;安装命令默认取得的版本可能已经更新。

英文术语中文名称简要解释
Coding Agent编码智能体能读源码、调用工具和委派任务的软件助手
Parent主控智能体分配工作、维护共享文件并汇总结果的角色
Hunter漏洞搜寻智能体在指定范围内寻找安全约束被破坏的证据
Verifier复核智能体不参与原始发现,负责反驳或核实候选结论
Coverage ledger覆盖台账记录每个审查单元看了什么、没看什么、为何停止
Coverage critic覆盖复查智能体寻找漏掉的入口、路径和未充分检查的单元
Trust boundary信任边界权限、身份或数据可信程度发生变化的位置
Fingerprint稳定标识跨裁决、跨轮次关联同一源码根因的标识
Harness执行编排系统把模型、工具、状态、调度与验证组织起来的运行系统

为什么不能只靠一次 Prompt

一次性 Prompt 可以帮助发现线索,却很难同时交代覆盖范围、保留每条推理的证据,并对自己的结论保持怀疑。当前 skill 为这些问题安排了不同角色:

  • Hunter 沿具体 Trust boundary 查找问题,而不是只匹配危险函数。
  • Coverage critic 检查遗漏,避免把「已经调用很多 Agent」当成充分覆盖。
  • Verifier 尝试推翻候选结论,尤其要重建路径上最强的现有控制。
  • Parent 把结果写成结构化记录,报告只能从最终记录生成,不能在写作时扩大影响。

Schema 校验通过,不等于漏洞成立。 两个 .cjs 脚本能拒绝不合约定的记录,不能替代源码复核和受限的本地行为验证。源码:validation 与 reporting

仓库里的哪些部分真正参与执行

这个项目的主要逻辑写在 Markdown 中。宿主负责理解指令、启动子任务和落实隔离;Node.js 脚本负责确定性的记录检查。不要把写在 Prompt 中的要求误认为仓库已经实现了对应的进程调度器或沙箱。

入口职责交给下一步的东西
SKILL.md选择运行模式、范围、profile 和预算,定义安全与写入边界本轮运行约束
RECONNAISSANCE.md从产品、身份、入口和执行环境四个方向建立源码地图architecture.md、初始 coverage-ledger.json
HUNTING.md按台账分派 Hunter,合并结果,再由 Coverage critic 查缺口带检查记录与候选标识的台账
ATTACK-CLASSES.md 与 companion 文档给不同 Trust boundary 提供检查方法和成立条件放入 Hunter Prompt 的具体文本块
VALIDATION-AND-REPORTING.md定义候选裁决、最终记录复核和报告生成findings.json 与三份报告
report-schema.json区分三类裁决允许和必须出现的字段机器可读的记录契约
两个 validate-*.cjs检查格式、状态约束、路径形式、排序与一致性通过或错误列表

这些文件的依赖方向很重要:Hunter 的自由文本不能直接成为最终报告。候选先经过独立裁决,形成符合 Schema 的记录,再接受最终复核,最后才转换成人读的文字。源码目录

六阶段怎样衔接

下面展示默认 standard profile 的主线。quick 会合并两次独立验证的职责,但不会取消独立验证。

flowchart TD
    A["Phase 1 · Reconnaissance"] --> B["architecture.md + coverage-ledger.json"]
    B --> C["Phase 2 · Coverage-led hunting"]
    C --> D["Coverage critic"]
    D -->|"发现范围内缺口且预算允许"| C
    D -->|"通过最终覆盖复查或明确提前停止"| E["Phase 3 · Candidate validation"]
    E --> F["Phase 4 · findings.json + validators"]
    F --> G["Phase 5 · Independent record verification"]
    G -->|"实质替换:新的独立复核"| H["Fresh verifier + validators"]
    H --> G
    G -->|"保留记录均已复核"| I["Phase 6 · Target-neutral reporting"]
    I --> J["REPORT.md"]
    I --> K["FINDINGS-DETAIL.md"]
    I --> L["NEEDS-VALIDATION.md"]

Phase 1:先建立范围,再开始找问题

Parent 先记录 run-metadata.json,包括源码 ref、工作树状态、profile、scope 和预算。四个基础 reconnaissance 任务分别读取产品与技术栈、身份与权限、输入与 sink、本地执行条件与部署可见性;这一阶段不运行目标代码,也不查询线上控制面。

architecture.md 控制在约 1,000 个英文词以内,给后续 Agent 共用;具体分工放在 coverage-ledger.json,而不是塞进越来越长的架构摘要。

台账按入口、Trust boundary、子系统和 attack class 的组合建立单元,必要时加 lifecycle。canonical_refs 使用源码身份,例如路径与导出范围,而非容易变动的显示标题。coverage_id 把这些引用按 RFC 3986 做 UTF-8 百分号编码,以 :: 连接;不把轮次、Agent、严重度或行号放进身份。这让「同一对象改了显示名称」不必变成一个新单元。源码:RECONNAISSANCE.md

Phase 2:Coverage-led hunting,而非全员扫全仓

Parent 从台账选择 planned 单元,指定唯一 agent_id,再把状态改为 in_progress。Hunter 获得架构摘要、分配给自己的 ID、源码起点、选中的 attack-class 原文,以及排除范围和理由。只发送 companion 文件名不满足协议。

Hunter 返回的每个单元必须对应一次分配,并列出 reviewed_paths、checks、候选 Fingerprint 或阻塞原因。Parent 把 checks 写入台账的 local_checks;这个字段也允许 method: "source" 的源码检查,不能望文生义地当作「所有单元都运行过测试」。

每轮之后由新的 Coverage critic 查遗漏。在 standard 和 deep 中,即使 post-wave critic 没有提出工作,也还需要一次不同 Agent 执行的 final-clean critic。发现缺口但没有预算继续时,要保留 deferred 或 out_of_scope,不能把它们涂成 covered。源码:HUNTING.md

Phase 3–5:先裁决候选,再复核最终记录

Phase 3 按 Fingerprint 和根因去重,把每个唯一候选交给没参与发现的 Verifier。Verifier 不接收其他 Verifier 的结论,要重读引用的当前源码,并在安全条件允许时独立复现决定性结果。它返回一个 JSON 对象,而不是一段「大致同意」的评论。

Phase 4 由 Parent 写出 findings.json,按 Fingerprint 排序,然后运行两个 validator。Schema 设置了 additionalProperties: false,Hunter 的包装字段不能顺手带入最终记录。

Phase 5 检查的是最终结构化记录。confirmed 和 needs_validation 都要复核:错误的待验证线索同样会浪费维护者时间。若替换记录提升了裁决、改变了根因、路径、输入、观测结果、影响或严重度,要再交给新的独立 Verifier,不能由提出替换的人自行签字。预算或独立性不足时,争议记录退出 findings.json,留在台账中并将本轮标为 incomplete。

Phase 6:报告是记录的展示,不是第二次创作

产物读者应从中获得什么
REPORT.md本轮范围、profile、源码版本、限制、confirmed 表、待验证线索、hardening 建议与覆盖摘要
FINDINGS-DETAIL.mdmedium、high、critical 的 confirmed 记录的完整路径、最小本地验证与修复建议
NEEDS-VALIDATION.md每条待验证记录的准确 blocker,以及适用的本地或维护者观察计划

Target-neutral 指使用目标自身的接口描述验证:库可以是函数调用,CLI 可以是命令,解析器可以是文件 fixture,基础设施可以是本地渲染的策略。HTTP 只是其中一种,不应为每个目标硬编一个线上 endpoint。报告不能提升裁决或严重度,也不把 rejected 当成漏洞列出来。

三种 verdict 不是三个置信度档位

Verdict成立条件记录中的关键字段不允许的混用
confirmed完整源码路径与受限本地执行证据支持了具体边界失败和观测结果root_cause、intended_behavior、conditions、execution、remediation、severity、confidence不能用一个未解决的 blocker 代替证明
needs_validation源码支持具体假设,但缺少一个决定性事实claimed_root_cause、trace、evidence、blockers、validation_plan没有 severity,也不带 confirmed 的 execution 或 root_cause
rejected源码、可见控制或本地行为反驳了候选主张claimed_root_cause、trace、evidence、reason不是低严重度漏洞,不带 severity

needs_validation 不是「没来得及看」的收件箱。若某条候选根本没经过 Phase 3 独立裁决,它只能留在台账的 candidate 单元里,不能为了凑齐输出而写成 needs_validation。后者必须有准确的 blocker,以及至少一个适用、非空的 validation_plan.local 或 validation_plan.deployment。

严重度只用于 confirmed。Schema 将 likelihood、impact 和 overall_severity 分开;脚本要求 overall 不高于已证明的 impact。这里不是执行一个固定的数值乘法公式,而是要求优先级不能超出证据支持的影响。源码:report-schema.json

一个假设场景:同一线索为什么会得到不同裁决

假设 Hunter 在一个演示用文档服务里发现:单条读取检查了 owner,批量导出似乎只检查登录状态。这里只说明协议,不提供真实目标、测试数据或漏洞结论。

  1. 若 Verifier 继续追踪后发现导出底层查询已经强制按 owner 过滤,原先「跨用户导出」的主张应为 rejected。外层少一次重复判断最多是 hardening 讨论。
  2. 若源码确实支持这条路径,但决定性的运行时策略不在仓库中,裁决可以是 needs_validation,并明确请维护者观察哪条策略,而不是请 Agent 去线上试探。
  3. 只有完整路径和隔离环境中两个 dummy principal 的最小结果证明了越权,才符合 confirmed 的证据要求;影响描述不能从一次错误记录读取扩写成整个数据存储失陷。

同一根因的 Fingerprint 不随 verdict 改变。下一轮仍要核对源码、条件和旧证据:未变的 prior confirmed 可以作为候选带入重新验证;源码已变的记录需要重新审查。旧的 blocked、deferred 或 needs-validation 状态从来不等于已经覆盖。

两个 validator 到底验证了什么

Findings:Schema 之后还有语义约束

validate-findings.cjs 是只支持本仓 Schema 所用关键字的零依赖解释器,不是通用 JSON Schema 实现。它的 CLI 调用链可以直接从 run(file) 读出来:

text
run(file)
  → loadSchema(report-schema.json)
  → readFileWithinLimit(file)
  → enforceJsonTextLimits(contents)
  → JSON.parse(contents)
  → validateDocument(findings, schema)
      → collectSchemaErrors(schema)
      → collectDataLimitErrors(findings)
      → collectUnchecked(findings, schema)
      → collectFindingSemanticErrors(findings)
  → PASS / FAIL,返回退出码 0 / 1

输入首先必须能以 no-follow、nonblocking 方式打开,且为普通文件;字节数最多 5 MiB,JSON 嵌套最多 64 层。语义检查包括 Fingerprint 唯一性与排序、安全的仓库相对路径、正整数行号、trace 首尾类型,以及 verdict 专属字段。多步 trace 从 entrypoint 开始,以 sink 结束,中间只能是 propagation。

因此,把一条本来结构合格的 needs_validation 记录加上 severity,会被 Schema 和语义规则拒绝;把空白字符串填进验证计划,也不能冒充已提供下一步。

但这个 CLI 只接收 JSON 文件路径,没有目标仓库参数。它不打开 trace.file 核对对应行,更不会运行记录里的验证步骤。一个格式正确、内容虚构的源码引用仍可能通过机械检查;Phase 3 和 Phase 5 正是为此存在。

Coverage:状态必须有匹配的证据

validate-coverage-ledger.cjs 不依赖 findings Schema。validateDocument() 先执行文档限额检查,再逐项调用 collectUnitErrors(),核对 canonical ID、状态与证据、历史 attempts,最后检查重复 ID、语义冲突和排序。

台账状态需要的证据形态
planned未分配 owner,检查与候选为空
in_progress有 owner,但尚无终态检查结果
covered有 owner、非空路径与 checks,没有未决问题或候选
candidate有 owner 和检查证据,并关联非空 Fingerprint
blocked有 owner 和部分检查证据,有 blocker,但没有候选 Fingerprint
not_applicable、out_of_scope、deferred未分配 owner、无检查证据,但必须说明原因

这里有两处容易漏读的约束。首先,unit 的 reviewed_paths 必须恰好等于各 check 路径的并集,不能随手多写一句「也看过认证模块」。其次,每个 check 有自己的 agent_id:Hunter 和 Verifier 可以向同一个 unit 提供不同 owner 的证据。源码检查用 artifact: null;本地检查的路径必须落在对应 agents/<agent_id>/artifacts/ 下。

当 Coverage critic 要求重做一个已有证据的单元,旧结果应追加到 attempts,新轮次使用新的 owner 与空的 live evidence,不能将旧证据复制成新 Agent 的工作成果。

这些检查约束的是 JSON 中的声明。该脚本并不核验 artifact 实际存在、内容真实或由安全流程提升而来,也不加载另一个 findings.json 做跨文件关联检查。文件真实性、两份记录的关联以及检查是否充分,仍由 Parent 与独立 Verifier 负责。

Attack classes 怎样选择

基础 ATTACK-CLASSES.md 覆盖 Injection、Access control、Resource and file handling、Cryptography and secrets、Business logic、Feature abuse and data leakage、Chained vulnerabilities and trust boundaries,另有 Wildcard 与 Obvious things。它们提供查找方向,不是「出现关键词就报告」的规则。

当前提交另有十份 companion 文档。选择依据是 reconnaissance 找到的 Trust boundary,而不是语言或依赖列表里碰巧出现某个名称。

Companion 文件对应的检查边界
MEMORY-SAFETY-AND-BINARY.md原生内存、二进制解析和内核相关路径
AI-AND-LLM.md不可信内容经过模型、memory 或工具调用后,获得何种权限或进入何种 sink
WEB-PROTOCOL-AND-AUTH.mdHTTP framing、缓存、会话及身份协议的绑定关系
CLIENT-SIDE.md浏览器输入、DOM、跨窗口消息、origin 和持久状态
SUPPLY-CHAIN-AND-RELEASE.md依赖解析、CI、构建产物、签名、发布与更新
CLOUD-AND-DEPLOYMENT.mdIAM、基础设施、容器、serverless 与部署配置
PROTOCOLS-RPC-AND-MESSAGING.mdRPC、序列化、消息队列、broker、webhook 与 streaming
RESOURCE-EXHAUSTION-AND-AVAILABILITY.md共享资源、配额、队列、worker 和运营方成本
DATA-ISOLATION-AND-LIFECYCLE.md租户隔离、派生副本、导出、删除、迁移与恢复
DESKTOP-MOBILE-AND-LOCAL-IPC.md原生应用、deep link、webview、helper 与本地 IPC

每份 companion 除了分类,还带有 Core discipline、Universal moves 和 Validation rules;选中它的某个类别时,相关约束也要一起进入 Hunter Prompt。源码:attack-class 入口与选择规则

例如 AI-AND-LLM.md 明确区分「模型被文字影响」与「真正跨越权限或 action-binding 边界」:Prompt injection 本身不足以成为 finding,必须指出确定性控制在哪一处失效,以及哪个 principal 或资源受到影响。CLOUD-AND-DEPLOYMENT.md 则提醒,源码中的配置意图不等于线上配置事实。前者防止把模型行为夸大成漏洞,后者防止凭仓库里没写某项配置就断言生产没有防护。

隔离、预算与反模式

隔离不是一句「不要联网」

当前 SKILL.md 要求目标控制的构建、测试、浏览器、emulator、fuzzer 和 fixture 处理只能在 OS-enforced sandbox 中执行:禁用外网,必要时仅允许隔离的 loopback;环境变量从空环境按白名单填入;源码和工具只读;只允许写入分配的 scratch;CPU、内存、进程数、文件大小、磁盘和耗时均有限额。缺少任一控制,就不能执行目标代码。

Parent 是共享文件的唯一写入者。每个子 Agent 只写自己的 scratch;保留证据的 artifacts/ 不暴露给目标进程。目标进程全部退出后,可信 Parent-side 代码才按预声明白名单提升证据,逐层使用 no-follow、目录描述符和普通文件检查,控制单文件与累计大小,拒绝符号链接、硬链接和特殊文件。直接递归复制 scratch 不符合协议。

仓库给出了这些要求和操作流程,没有提供一个安装后自动生效的完整沙箱或 artifact promotion 服务。执行宿主必须落实它们;Prompt 承诺不能替代操作系统控制。不能落实时,保留准确 blocker,而不是在宿主环境偷偷运行。源码:SKILL.md 的执行与写入边界

预算减少广度,不降低证明标准

quick 只有一轮 Hunter 和一次最终 Coverage critic,Phase 3 与 Phase 5 共用一个新的独立 Verifier;critic 新发现的工作留下 deferred。standard 保留两次验证和完整覆盖复查流程。deep 按子系统与 lifecycle 细分,并对 prior same-source covered 单元增加独立复查。

严格预算按 Agent invocation 计数。开始 reconnaissance 前就要为四个基础任务、必要 critic 和至少一个 Verifier 留足预算;后续每轮仍要先预留 critic 与 validation 成本。预算耗尽时,声明 incomplete 并列出未裁决候选,不能静默超支,也不能把没审完的候选包装成正式 finding。

三个常见误用

  • 把加固缺口当成漏洞。 缺少某个 Header 或第二层校验,不代表第一层控制已经失效。必须追到具体边界和结果。
  • 把缺少环境事实当成确认或否定。 不可见的 proxy、IdP、broker ACL、部署策略,既不能假设存在,也不能假设不存在;需要准确的 blocker。
  • 把多 Agent 当成绝对独立。 新上下文能减少同一会话的自我确认,但不保证模型没有共同盲点。来源、隔离、反证和人工复核仍然重要。

此外,没有 confirmed finding 只表示本轮范围和证据下没有确认问题,不构成「系统安全」证明。它不能替代专业渗透测试、威胁建模、人工安全评审或生产环境配置核验;更不授予测试第三方系统的权限。

安装与触发:仅说明,不在本文执行

上游 README 使用 Skills CLI 安装指定 skill:

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

默认安装到当前项目;加 --global 才是用户级安装。具体 Agent 选择和非交互选项可查看 npx skills --help。安装前先审阅将要加载的版本;这条命令本身不会为宿主建立 OS sandbox,也不会启动审计。Cloudflare README · Skills CLI 安装选项

在具备 tool use、并行子 Agent 和所需隔离能力的宿主中,可以用 README 的典型请求触发完整流程:

text
security audit this codebase

要给授权范围加上约束,可以写成:

text
对我有权审查的本地演示仓库执行 security audit。
使用 quick profile,只检查 ./src;输出到仓库外的独立目录。
禁止访问部署端点、外部服务和真实身份;仅使用 dummy 数据。
目标代码只有在所需 OS sandbox 控制全部满足时才能运行。
未覆盖范围和未解决事实必须明确记录,不要声称完整覆盖。

Full audit mode 默认输出到 ~/security-audit-skill/<repo-name>/run-<N>。只有用户明确选择仓库内目录,且 Parent 确认整个目录被版本控制忽略时,才允许写进目标仓库。安全问题咨询、方法讨论和局部线索分析默认使用 guidance mode;加载 skill 本身不授权创建审计产物。

对已由授权流程产生的记录,Parent 使用下面两个命令;它们只是记录校验,不启动目标审计:

bash
node <skill-dir>/validate-findings.cjs <output-dir>/findings.json
node <skill-dir>/validate-coverage-ledger.cjs <output-dir>/coverage-ledger.json

台账初始化和之后每次更新都要校验,findings 在 Phase 4 及 Phase 5 每次应用替换后重新校验。不要把一次 PASS 当成之后所有编辑仍然有效。

它与 Cloudflare vulnerability harness 的关系

Cloudflare 的 Build your own vulnerability harness 发布于 2026-06-18,回顾的是从单仓 skill 演进到 fleet-wide 系统的过程。博文描述早期约 450 行、七阶段的 skill,以及三个 reconnaissance Agent;本文固定提交中的版本已经是六阶段、四个基础 reconnaissance 任务,报告从最终记录生成,也没有将提交 ingest API 列为最后一步。两者属于不同时间的设计,不能拼成一套安装步骤。

博文把完整系统分成 Vulnerability Discovery Harness(VDH)和 Vulnerability Validation System(VVS):前者发现线索,后者继续去重、裁决和修复,并使用不同模型交叉检查。它讨论了数据库保存状态、可恢复执行、跨仓依赖追踪和持续调度,这些不能归到这个公开 skill 的现成功能里。

当前 skill 是单仓起点,不是完整的 fleet-wide Harness。它已通过文件持久化覆盖和裁决,并要求跨 run 复核旧证据;但没有直接提供数据库任务队列、企业级 ingest API 或全组织扫描服务。

官方提到,在他们的试验中单轮大约只能发现多轮总计发现问题的一半。这是该团队对其试验的观察,不是这个 skill 对任意仓库的召回率承诺,也不是本文测得的结果。它更适合支持一个有限结论:重复运行要能利用旧证据和缺口,不能只是多发几遍相同 Prompt。

方式主要价值不能自动获得的东西
一次性 audit Prompt快速获得线索;质量取决于 Prompt 与宿主明确覆盖账本、独立裁决和可恢复状态不一定存在
当前 security-audit-skill单仓、多阶段、结构化证据与 verdict 协议已落实的 OS sandbox、生产配置事实、完整 fleet 编排
博文中的 vulnerability harness持久调度、多轮调查、跨仓关系与后续 triage对所有漏洞的穷尽证明,或对人工判断的完全替代

对想借鉴这套设计的团队,最值得先学的是证据边界:谁能改共享记录,什么可以算 covered,什么必须留作 blocker,何时需要另一位 Verifier。只有出现持久任务、跨仓依赖或大量重复候选的实际需求,再建设相应的 Harness 层;不必为了使用一份 skill 先复制整套平台。

参考与关联阅读

博客助手

正在打开博客助手…