找到,从来不是问题
修复才是
在自己的代码和开源软件里查找并修复漏洞,Anthropic 安全团队得出的首要结论是:漏洞「发现」现在已可直接并行化,瓶颈已经转移到了验证、分诊与修补。
截至 2026 年 5 月 22 日的开源扫描数据。这道落差正是全文要解决的问题——发现已可廉价并行,瓶颈整体后移到了「找到之后的一切」。本文的骨架就是一个循环:两步一次性投入,托起一个反复运行的防御者循环。
两步一次性设置,四步反复运行
查找并修复漏洞最多的团队,都收敛到了对既有最佳实践的一种变体。前两步是一次性设置(每个代码库做一次、系统变化时复审);后四步是针对源代码反复运行的循环。
关于运行节奏:首轮发现数量通常最高;后续更少、但更复杂。但不要指望第 n 轮零新发现——模型是随机性的,大型代码库即便代码不变也有长尾漏洞持续涌出。首次迭代应多次运行,依据净新增发现和风险容忍度决定何时停止;之后周期性扫描、或代码有意义变更时扫描。
误报头号成因:模型不懂你的信任边界
模型可能把代码标为有漏洞,因为它假设客户端会发送损坏的值、或攻击者能控制配置——尽管在你的环境里这些输入是受信任的。反过来,它也可能把面向互联网的服务当成仅内部使用,从而漏报真实漏洞。两种情况下,模型错的是威胁模型,不是代码。
When the threat model was well-defined, the model's findings "were exploitable 90 percent of the time."
两步构建威胁模型
① 从代码、文档和漏洞历史 bootstrap。把你会在第一天交给一名新安全工程师的东西喂给模型:架构文档、wiki、入口点、git 历史、过往漏洞。让模型产出含系统上下文、资产、入口点、信任边界的威胁模型,并对过往 bug 聚类、列出漏洞类别。务必记录:你在意哪些漏洞、不在意哪些、为什么。
"'What have people exploited in the past' is sometimes a much easier cheat-code towards success than 'find me vulnerabilities in this codebase.'"
② 让模型采访熟悉系统的人。参考 Shostack 四问:我们在构建什么?会出什么错?我们对此做了什么?我们做得好吗?先跑 bootstrap,受访者就从草稿而非从零开始。采访可选,但能补上模型从代码/文档拿不到的上下文。
几个能带来明显差异的实践
- 考虑依赖的安全策略。许多开源项目已发布(如 vLLM 的 security.md、SQLite 的 "Defense Against the Dark Arts"、ImageMagick 安全策略)。直接纳入,而非从头重建。
- 明确写出什么是受信任的。信任配置文件或已认证客户端,就写进威胁模型——帮你把「不可利用的 bug」和「真实漏洞」区分开。
- 在代码里放一个
THREAT_MODEL.md。随代码更新,发现 agent 搜索前先读它,跳过已知非问题。
威胁模型用在两处:发现阶段作为范围(分区、排优先级、跳过范围外);分诊阶段作为过滤器(把严重级校准到你的系统)。
good context of the code, but not good context of us.
bootstrap 从代码、CVE、git 历史导出草稿,interview 用 Shostack 四问精炼。产出 THREAT_MODEL.md,供发现与分诊使用。
沙箱的两个用途:保护系统,与证明可利用性
用途一 · 保护系统。要让模型安全、自主地运行,你需要强隔离层。没有它,agent 可能越过目标、做出意料之外的事。
One team told the model it had no network access—when it actually did—and the model discovered it could fetch from GitHub anyway. Another team observed an agent answer a GitHub issue mid-scan.
让隔离匹配威胁模型:只读代码的发现 agent 用容器就够;但目标程序及其 PoC 要跑在 microVM(如 Firecracker)或完整 VM 里、锁死出站。绝不要让凭证(~/.aws、~/.ssh、.env)对 agent 可见。
网络访问只在搭建时开放
每次运行开始时加载快照,使每次扫描都从同一干净起点开始。
用途二 · 证明可利用性。静态扫描时模型只能假设什么会坏,无法测试路径是否可达、是否有补偿性控制。当团队搭出能让 agent 编译、跑测试、引爆 PoC 的沙箱后,不可利用的发现显著下降。
"the biggest efficacy lever has been giving the model test beds, live systems, and running the PoCs."
尽量 pin 住一切:镜像标签、commit SHA、依赖、构建命令,让每次运行用相同代码、相同环境。
they now build Docker containers with dependencies pinned to match production, so the finding agent and the verification agent operate on the same artifacts an attacker would.
沙箱要足够忠实于生产:排除依赖(如队列/数据存储)会漏报;忽略生产防御(如 WAF/认证网关)会报出已被缓解的不可利用项。
如果搭一个有代表性的沙箱不切实际(云依赖、数据存储等),就先从发现步骤开始。前沿模型仅凭分析源代码就很擅长找漏洞——包括 Anthropic 自己在内的多个团队都验证有效。代价在验证阶段:没有运行中的目标就无法用 PoC 证明,所以多留时间给验证。沙箱可以等发现量足够大、值得投入时再建。
参考实现:harness 的 README 提供参考沙箱——agent 与目标跑在 gVisor 隔离容器里,出站锁定到模型 API,目标从 pin 到特定 commit 的 Dockerfile 构建,由 setup_sandbox.sh 处理搭建。
丰富上下文、更短的 prompt、有用的工具
给发现 agent 它可按需加载的上下文:威胁模型、架构文档、过往扫描结果。当它理解你的信任边界和系统实际部署方式时,能更好地识别你系统特有的漏洞。
前沿模型在发现阶段从越来越简单的 prompt 中受益。违反直觉地,更规定性(prescriptive)的 prompt 反而让发现变差——长 checklist 往往降低模型创造力、生成更少的新颖 bug。
✗ 过度规定 prescriptive
- 长 checklist 逐条规定怎么扫
- 把「how to scan」写死
- 收窄模型的尝试范围
- 新颖 bug 更少
✓ 给目标与上下文 goal + context
- 说清 why / what:为什么扫、好发现长什么样、扫什么系统
- 「how to scan」留给模型
- 可指定一个具体漏洞类别(描述它做什么、住在哪)
- 定义结构化输出 + escape hatch(弱发现可提前退出)
给工具,也让模型自己造工具
给 grep、glob 等搜索/读代码工具;让模型用 SAST 扫描器、fuzzer 等安全工具;问它某任务需要什么工具再提供;并让它按需自己构建工具——近期前沿模型越来越擅长写自己需要的工具。
the agent didn't need to guess whether a path could be reached and could test each candidate against the running application as it went, improving their true-positive rate to nearly 100 percent.
分区 + 并行,而不是暴力堆量
先让模型对系统做一遍,按攻击面 / 端点 / 组件把搜索空间分区;再把分区喂给并行发现 agent,避免它们收敛到同样的浅层 bug;最后跑一遍系统级 pass,把分区发现作为上下文。
"We initially tried to just horizontally scale and send more agents, but saw limiting returns."
若有沙箱能跑目标,让发现 agent 为发现构建 PoC(脚本 / 崩溃输入 / 失败测试)——既帮 agent 锁定发现,也给验证 agent 提供证据。无法复现的发现仍可上报,标记为 unproven 以保持高召回。
读取 THREAT_MODEL.md,把目标分区为若干 focus area,为每个 area 扇出(fan out)并行 review agent。产出结构化发现,供下一步直接消费。
发现求召回,验证求精度——别让同一个 agent 兼任
当一个 agent 想在同一步同时做两件事时,它会自我审查(self censor),把可利用的真阳性也排除掉。
We learned this the hard way, where asking discovery agents to also verify findings led to them filtering out true positives that a separate verification step would have confirmed.
验证 agent 必须独立
在全新容器里运行验证器,没有共享文件系统、没有对话历史。若验证器暴露在发现 agent 的推理之下,它可能只会附和、而非测试主张。只给它两样东西:(1) PoC 或书面发现,(2) 代码库——好让它搜索发现者漏掉的缓解(上游校验、认证门、类型约束、不可达代码)。
若单遍仍放过太多,跑多个独立验证器(不同角度 / 不同模型)取多数投票;也可设独立 judge 在发现与验证结果间裁决。提示验证器去「证伪」:假设每个发现都是误报,搜索它为什么错。这在发现不含 PoC 时最重要。
若能在沙箱充分复现生产,就让验证器构建并执行可复现 PoC——PoC 成功即判定可利用。但反过来不成立:无法产出可用 PoC,并不能证明它是误报。
"Validation is the biggest holdup and the PoC is the validation."
模型午饭前能找到一百个候选,分诊成了新瓶颈
验证确认一个发现可利用,分诊评估修补优先级。过去找 bug 的人顺手就分诊了;如今发现量暴增,分诊成了瓶颈。提交太多重复或被夸大严重级的 bug 会引发告警疲劳——产品工程师会停止阅读,连需要立即修的也不看。开源维护者尤其容易被淹没。
if we send product engineers a pile of findings where a majority are non-exploitable, they will lose trust in the reports and give up.
去重:看根因
先做一遍便宜的确定性 pass(同文件、同类别、漏洞行号彼此十行以内),再让模型对剩下的应用定性规则:
当作重复 duplicate
- 同一根因的不同措辞
- 同一漏洞报在多个调用点
- 一个缺失的全局保护(如认证检查)被逐端点报出
- 同一路径里的「因」和它的「果」被分别标出
当作不同 distinct
- 同一文件里不同的漏洞类别
- 不同变量到达不同 sink
- 一个 helper 里两个独立的 bug
- 同一缺失检查出现在两个端点、但各需各自的修复
若 harness 为每个发现都生成 PoC 和补丁,另一种去重法:检查一个发现的补丁是否也解除其他发现的 PoC。
定级:六个维度的 rubric
让模型先写出每个问题的答案,再给严重级——先过证据能防止它锚定在 bug 类别上("SQL injection, so critical")然后抬高严重级去匹配。阈值按你的系统调整。
模型因上下文不足而过度自信:它可能不知道攻击者实际控制哪些输入、看不见上游 WAF 或认证。解法是在分诊时给模型威胁模型——澄清「we trust authenticated clients」就能移除一整类 critical。
同时做验证和分诊:每个发现多投票验证、跨运行去重、按导出的可利用性重排。产出一个简短、排好序、有归属的列表,而非原始转储。
修根因、找变体、最小化补丁——人仍然要 own 它
修补是闭合循环的地方,也用已验证的发现改进威胁模型、把过往发现喂进下一次扫描。每一轮都加固代码库,让下一次扫描信息更充分。
先写一个会失败的测试(TDD)
修补前,写一个在现有代码下会失败的新测试;实现修复后确认同一测试通过、且不破坏其他东西。不加测试,修复可能悄悄回退,事后也难证明 bug 是真的。
By giving the model feedback to iterate against, patch quality jumped, saving time on human review.
修根因,并在两个层面找变体
模型可能只狭隘地处理某调用点,简单提示它「识别并修复根因」往往就有效。然后找变体:① 同模式(same pattern)——别处的其他调用点或副本;② 同类别(same class)——有一处 SQL 注入的代码库往往还有更多。
上线前:对抗检查 + 最小化补丁
发补丁前跑一次对抗检查:让新的发现 agent 以攻击者视角探测补丁是否全面。再简化补丁——最小改动更易评审、更不易引入新 bug。提示「只做能修根因的最小改动,不重构、不顺手清理、不重格式化」。
"The recommended patches tend to be as restrictive as possible, to the point that they would break connections with other services. It would address the issue, but break the dependencies that allow the service to work in the first place."
补丁校验阶梯(从最便宜开始)
虽然模型能写补丁,但仍需一个人来 own 它。生成补丁会以可预测方式失败:修症状非根因、阻断合法输入、移除对依赖服务的访问。尽可能校验每个补丁,让人工评审聚焦于模型不知道的细微之处(即将到来的变更、代码风格)。
消费分诊输出,为每个发现生成候选 diff,并有一个独立 reviewer agent 检查每一个。
把 harness 连到事件上
Anthropic 相信,模型在代码中查找并利用漏洞正变得越来越容易。作为防御者,我们的工作是在对手利用之前找到并修复。有些团队甚至把 harness 连到了事件上:
- 一份 bug bounty 报告 触发一次自动的变体分析
- 一次 安全评审 触发扫描,并附上候选发现
- 一个 已验证的漏洞 更新静态分析工具,以防止它再次发生
done right, it's the start of a larger, more hopeful shift, where we'll be able to find and fix vulnerabilities before attackers exploit them.
上手 & 资源
克隆 defending-code-reference-harness,在 Claude Code 里 run /quickstart,在 demo 目标上走一遍从威胁建模到扫描到分诊的交互式工作流。该 repo 还含自主 harness 和 /customize skill。然后在你自己的代码上跑——先为扫描之后的流水线预留预算,再去扩大扫描。