几周前,我们发布了 Project Glasswing 的初步发现,探讨了把前沿安全模型对准企业代码库会发生什么。我们还介绍了防御体系如何调整,以保护我们的基础设施和客户免受前沿 AI 带来的威胁。此后,AI 生态仍在快速变化,围绕单一模型紧密构建系统的开发者已经体会到,当该模型不再可用或被更强的模型取代时会发生什么。这些市场变化进一步印证了我们的核心观点:无论某一天哪个底层模型领先,智能体工作流的未来都不会在独立的模型、提示词或单智能体会话中找到。
要从局部的安全“技能”(skill)走向持续运行、覆盖整个机群的扫描流水线,就需要一种把模型视为可互换组件的架构。依赖单一模型天然会限制防御覆盖面,因为同一个系统往往会用完全相同的视角去审视代码路径。为此,应当经常更换模型并进行交叉测试。通过在流水线中使用不同的模型,例如用一个模型做初步发现,用一个完全不同的模型做验证,我们可以确保漏洞由不同的逻辑体系交叉核查。此外,真正企业级的框架必须超越孤立的代码仓库,跨仓库依赖追踪漏洞,最终把成千上万的原始候选项过滤成一个可信的、经过分诊的可执行修复队列。
本文将实际介绍如何构建这一与模型无关的层,重点讲述我们如何管理状态控制、消除误报,以及如何大规模协调端到端的分诊。
先回应两种质疑
第一篇文章论证了为什么通用编码智能体做不了这件事。主要问题在于,智能体一次只能持有一个假设,在覆盖真实仓库的一小部分之后上下文窗口就被填满,随后在上下文压缩时丢失信息。详情请阅读那篇文章。
在继续之前,我们先回答两个很可能被问到的问题。
“为什么不用子智能体而要用框架?”子智能体很有用,也是不错的起点。但安全分析需要成百上千个相互独立的调查:它们要能跨多次运行保留下来,不共享上下文窗口,并且之后可以重新划定范围、相互交叉引用。这需要持久化、去重、可恢复性,最终还需要覆盖整个机群的依赖追踪。这是一个编排问题,靠提示词解决不了。
“这篇博客是不是只是前沿模型的广告?”不是。我们的方案以框架为核心,而不是模型。在漏洞发现上,我们会使用当前最擅长我们所需能力的前沿模型来运行。当我们把不同的模型对准同一个目标时,它们各自发现的漏洞比例并不相同。框架才是能长久存在的部分。如果你要构建自己的系统,请从第一天起就把它设计成与模型无关,这样你就可以自由选用任何模型,不受约束。
一切从一个技能开始
我们最初写了一个约 450 行的 security-audit 技能,在单个代码仓库上运行,并不断调整提示词,直到发现真正的漏洞。之后,我们加入了编排层,它成了整个系统的底层管道。真正的价值存在于提示词本身,我们的提示词至今仍几乎原封不动地沿用最初技能中的攻击者场景、漏洞类别和反模式检测。
这个技能被设计为在一次会话中运行 7 个阶段的审计:
- 三个并行的调研智能体进行侦察,并写出一份
architecture.md。 - 每个攻击类别运行一个猎手(Hunter)智能体,目标是攻破代码而不是评审代码。
- 对抗性验证者尝试推翻每一个发现。
- 幸存的发现被整理成人类可读的漏洞报告。
- 它们还会按照某个模式输出为
findings.json,并由机械检查来校验该文件。 - 最后,一个全新的智能体对照源代码独立地重新验证每个发现。
- 经过重新验证后仍然成立的发现被提交到摄取 API。
这个最初的技能几乎可以直接对应到后来的框架:
| 技能阶段 | 框架阶段 |
|---|---|
| 侦察智能体写出 architecture.md | 侦察(Recon) |
| 猎手按攻击类别运行 | 猎捕(Hunt) |
| 验证者推翻发现 | 验证(Validate) |
| 幸存的发现形成报告 | 报告(Report) |
| findings.json 只做机械的模式合规检查,不检查正确性 | 对发现中的行号和函数进行机械验证 |
| 全新的智能体重新验证发现 | 独立验证 |
这个技能确实管用,但很快就暴露了它的局限。从覆盖率指标来看,单次运行只能发现多次运行所能捕获漏洞的一半左右。以我们的经验,它发现的那些漏洞偏向更简单、不那么隐蔽的类型。一旦你的流程基本上变成了“跑十遍然后手工对比差异”,大概就该考虑一个真正的框架了。
在运行和调优这个技能的过程中,我们撞上了三堵墙:
- 上下文耗尽:运行一小时后,上下文窗口被填满,模型会蚕食自己的记忆,立刻忘掉它花了整个上午追查到的漏洞。我们通过把状态完全外部化、将 LLM 视为无状态的计算引擎,打破了这一瓶颈。
- 持久化:运行中途崩溃就得从头再来。因为一次 AI 速率限制错误或连接不稳定就损失数小时的工作,是意识到自己需要更好架构的一种代价极高的方式。
- 跨仓库推理:单仓库会话完全看不到使用该仓库的各个应用之间的关系,而在检查组件之间的接口时暴露出的漏洞数量,可能比人们预想的要多。
建议:一个真实但最小化的框架只需要侦察、猎捕和验证三个阶段(保存在数据库中),外加一个不能自己提交发现的独立验证者(Validator)。在你拥有多个重要仓库之前,完全可以跳过跨仓库追踪。在你真正被噪声淹没之前,也可以不设专门的去重智能体。先在你的开发环境里从一个技能做起,把提示词调好,只有当缺少下一个架构阶段确实成为拖慢你的具体因素时,再去构建它。
把技能固化为流水线
这一领域大多数 AI 安全文章讲的都是单个仓库或精心挑选的基准测试;以这种方式运行整个机群,并带有跨仓库追踪,我们还没见过别处有类似的文章。我们的代码库涵盖大量不同的语言,包括 Rust、Go、C、Lua、TypeScript 和 Python,此外还有各种配置管理系统、静态配置以及各类额外的上下文。所以我们必须想出适合自己的新办法。从第一次斜杠命令运行,到能够覆盖 128 个不同仓库、自动发现并审查相关依赖的机群扫描器,大约花了六周时间。固化过程基本是机械的:我们把技能的每个阶段提取成各自独立的智能体,在后面放一个数据库,前面放一个编排器。两者几乎是一一对应的。
整个机群运行在同一个统一的框架上,不做按语言的调优,并能追踪仓库之间的依赖。把语法处理交给模型虽然使系统与语言无关,但真正的差异化优势在于它能追踪仓库之间的依赖。框架本身并不在乎它看到的是 C 指针还是 TypeScript 文件,它关注的是安全编排的更高层逻辑。这使我们能够扩展到数百个不同的代码库,而无需编写自定义的语言解析器。
两阶段漏洞研究工作流
我们整个漏洞研究工作流建立在两阶段的运作框架之上:漏洞发现框架(Vulnerability Discovery Harness,VDH)和漏洞验证系统(Vulnerability Validation System,VVS)。
VDH 是我们的发现引擎,主动扫描代码库以暴露潜在的安全问题。漏洞进入 VVS 后(VVS 允许多个框架向其输入),会依次经历去重、判定,最后是修复,这些我们稍后会讲到。
我们让 VDH 使用一个模型,而 VVS 使用一个完全不同的模型,这样两个模型实际上在互相复核。这样做有明显的安全收益:强制模型 B(VVS)去评判模型 A(VDH)的输出,就能确保发现由一套完全不同的逻辑权重和训练数据来评估,它扮演着无偏见的对抗性第三方,唯一的任务就是无情地对模型 A 的假设进行压力测试。在运维层面,把模型供应商当作可互换的商品也让我们受益。模型供应商可能随时间调整温度、缓存和推理算力预算,即使在同一个模型版本内也是如此。我们没有构建一个依赖模型行为长期可预测的系统,而是让框架能够吸收下游的波动而不至于崩溃。
第一阶段:漏洞发现框架(VDH)
第一篇文章已经讲过各个智能体/阶段的用途,所以这里谈它没有涉及的部分:阶段之间的衔接,以及决定整套系统能否奏效的若干细节。
| 智能体/阶段 | 主要职责 | 子智能体 / 工具 |
|---|---|---|
| 侦察(Recon) | 梳理目标架构,并梳理潜在的威胁向量 | 3 个并行的侦察子智能体写出 architecture.md |
| 猎捕(Hunt) | 按类别发起攻击、编译代码片段、探测二进制文件 | 它会派生同级智能体(视模型不同,占整个机群任务的 9% 到 20%)。它会访问并写入愿望清单(Wishlist)工具。 |
| 验证(Validate) | 先机械地检查发现,再以对抗方式尝试推翻它 | 分两遍运行:先由普通代码处理初步的模式/路径检查,再由一个独立隔离的智能体在发现被提交之前尝试推翻它。 |
| 补缺(Gapfill) | 为空白的覆盖单元格生成新的猎捕任务 | 为任何仍显单薄、测试不足的(区域 × 攻击类别)单元格加入新的猎捕任务 |
| 去重(Dedup) | 识别并合并重叠的发现 | 结合确定性代码与智能体,按根本原因对发现进行聚类,并实时将它们合并 |
| 追踪(Trace) | 遍历依赖图;派生下游使用方仓库的任务 | 遍历依赖图,在每个已识别的下游使用方仓库内加入猎捕任务,确保跨仓库的漏洞不被遗漏 |
| 反馈(Feedback) | 从已有报告中学习,并优化后续运行 | 收集验证失败、过浅的运行和反复遗漏的情况,并立即重写队列中的提示词,让后续任务更有针对性。 |
| 报告(Report) | 生成人类可读的报告 | 只是一个脚本,不需要模型 |
表 1:漏洞发现框架(VDH)
第四到第八阶段作为持续的生产者-消费者循环运行。随着初始猎捕的推进,补缺、反馈和追踪智能体不断生成新任务;去重把重叠的发现重新合并,其余环节则持续消费队列。这确保了在周期后期发现的漏洞,仍能在同一次运行内得到验证、报告,并与其他代码比对以确认其中没有相同的漏洞。
这样拆分流水线可以保证严格的上下文控制。一旦上下文窗口被填满,模型就会开始产生幻觉。我们让每个智能体的任务高度聚焦,把上下文用量控制在整个窗口的 25% 以下。简单粗暴的“读取所有文件”做法每一次都会轻易突破这个限制。
让我们吃过亏的一点是:持久化必须先于并行化考虑。你不会希望因为一个意料之外的错误就丢掉一次五小时的运行。每个阶段都写入同一个 SQLite 数据库,以 (run_id, repo, stage) 为键。任何阶段都可以恢复、重试,或被拉入后续的运行,而无需重做已完成的工作。发现是流式生成并即时保存的,因此崩溃只会损失正在处理的那个任务,别无其他。
建议:有时,一个暂时性的 API 错误会作为文本出现在 (200 OK) 响应流中,而不是抛出代码异常。对编排器来说,这看起来与正常完成的任务毫无区别。你必须显式地对响应文本进行分类,而不能只相信异常类型,否则就会把空跑记录成成功。
动态威胁建模
在侦察阶段,由智能体自己撰写威胁模型,而不是被动接受一个现成的模型。除了大约十种内置的攻击类别(多种形式的注入、内存破坏、协议解析、时序侧信道等)之外,侦察智能体还可以当场发明针对具体仓库的类别,并为每个类别设计各自的方法。它会写出专门针对该代码库的定制分类体系,用来更严格地限定猎手智能体的范围。
仅靠阅读源代码,不足以理解代码在压力下的行为,尤其是 C 和其他较底层语言中那些隐蔽的未定义行为漏洞。猎手智能体超越了代码阅读,转入主动执行。它们编译代码片段、构建小型版本,并对其发起攻击。质量上最大的飞跃来自给猎手提供一个沙箱(基于 unshare 构建),用来使二进制程序崩溃。
建议:如果框架本身运行在 Docker 中,这个沙箱需要 seccomp=unconfined 和 apparmor=unconfined,否则会悄无声息地启动失败。这只需要改一行配置,却能让像我们这样不是嵌套容器化专家的人省下一整天的头疼。
微分叉与愿望清单
除了核心流水线阶段之外,我们还增加了两种专门机制,赋予猎手相当大的自主权,使其能够调整关注点并请求外部资源,而不会打断正在进行的分析:
同级分叉(Sibling Forking):这有助于确保当猎手智能体偶然碰到当前范围之外的有趣代码路径时,不会偏离主线。它通过工具调用,用一个精确的结构化种子分叉出一个同级智能体。在整个机群中,这大约占任务的 9%,不过该比例高度依赖模型,从接近零到约五分之一不等,取决于是哪个模型在执行猎捕。
愿望清单(Wishlist):当智能体需要一个自己没有的工具时(通常是验证者要确认概念验证(PoC),或猎手想搭建某种东西,比如特定的构建环境、虚拟机或一些生产配置文件),它会写入一个中央愿望清单。它会提供足够的上下文,使系统在人类提供该依赖之后能够自动重新运行那个确切的任务。其中一部分可以实现部分自愈:如果需要修改并重建容器,可以在运行结束后由一个通用编码框架监控日志,自主完成。
自愿望清单上线以来,它在 128 个仓库中被写入了 25,472 次,是智能体向我们反馈的主要渠道。我们写这篇文章时恰好收到一条:“我需要一台 FreeBSD 虚拟机来端到端地确认这个 PoC。”
覆盖整个机群的跨仓库追踪
在初步清理之后,追踪者(Tracer)智能体会检查不同软件组件之间是如何连接的。它寻找一条特定的路径:潜在的攻击者能否从外部把有害输入送到系统中存在漏洞的部分?如果答案是肯定的,追踪者智能体就会自动在下游使用方仓库内派生新的猎捕任务。要做到这一点,你需要一个统一的跨仓库符号索引和一张准确的依赖图。这样才能发现标准的单仓库扫描会漏掉的深层系统性缺陷。
在整个机群的仓库上运行我们的框架,让我们得到了两条只有大规模运行时才会浮现的经验。
第一,去重本身就是一个独立的问题,大到需要专门的智能体来处理。当你只扫描少数几个仓库时,可以人工肉眼比对重叠的漏洞。简单的字符串匹配或文件路径检查在这里帮不了你。判断两个复杂的逻辑缺陷是否真的是同一个根本漏洞,听起来简单,其实不然。这需要大量的认知推理,以至于我们不得不部署专门的去重智能体来清理噪声,并为它们配备各自的启发式规则和减少工作量的方法。
第二,不要过早接入静态分析。我们把 Semgrep 一路接了进去,结果在一个月的运行中,猎手们调用它的次数为零。它们更愿意直接阅读并运行代码。相比之下,愿望清单是系统中使用最多的单一工具。值得关注的是智能体实际会去用什么,而不是你以为它们会想要什么。
让发现值得信赖
智能体会修改源代码让自己的利用代码生效,然后得意洋洋地报告它刚刚制造出来的漏洞。它会写出一个完全是同义反复的测试来“证明”某件事,比如“exec() 会执行东西,所以是严重漏洞”。或者它构造出一个能正常运行但什么也证明不了的利用,因为背后的威胁模型本身就是胡说八道。如果你的框架不主动对抗这些情况,你构建出来的不过是一种更快产出垃圾的方式。
猎手必须先陈述威胁模型,才被允许提交任何发现。它必须明确定义攻击者是谁,以及该漏洞跨越了哪条边界或打破了哪个假设。输出模式中字段的顺序强制了这一点。这项要求消除了空洞的发现,也就是那种“如果用户拥有数据库写权限,他们就可以写数据库”之类的发现。
每个经确认的发现都附带一个以测试形式编写的 PoC,它针对原始、未经改动的代码库运行。这可以防止智能体通过修改源文件来强行让利用生效。如果没有可运行的 PoC,我们就将该发现视为伪造。实际中,这表现为猎手编译一段三十行的解析循环,在启用内存保护的情况下运行,并证明错误的读取步长来自栈地址,而不是预期的消息体。你可以自己重新运行它。此外,每个经确认的发现还必须附带一个拟议的补丁。真正进入我们评审队列的,是一个经过验证的漏洞、一个可运行的测试,以及一份可用的 git diff,而不只是对问题含糊的文字描述。
在一条利用路径得以保留之前,确定性代码(用普通代码而不是另一个模型编写)会机械地验证所引用的文件和路径确实存在,并确认补丁和测试都能正确解析。这个验证者不能自己登记发现;它唯一的任务就是全力推翻猎手的理论。如果允许猎手给自己的作业打分,它会信心十足地认可自己输出的一切。
我们不对自己的系统声称某个漏报率。没有一个标注了代码库中所有真实漏洞的数据集,所以任何声称的召回率数字都纯属臆测。我们能观察的是:重复运行是否仍在不断发现新漏洞(确实如此),以及跨多次运行的覆盖率是否仍在增长。这些都只是代理指标,因为你无法确切知道单个代码库里到底有多少漏洞,但作为衡量有效性的方式已经足够好。
第二阶段:漏洞验证系统(VVS)
框架产出的发现只是分诊流程的开端,所有发现都汇入同一个共享的 VVS,目前共收录了 145 个仓库的 13,841 条发现。对如此规模的发现做分诊本身就是一个庞大的工程问题,其重要性不亚于猎捕。这个分诊引擎运行在与框架不同的模型上,分为三项各自独立的工作。
| 智能体/阶段 | 主要职责 | 派生 / 子智能体 / 工具 |
|---|---|---|
| 去重(Dedup) | 识别某个漏洞是否已在系统中,或已作为内部 Jira 工单提出 | 确定性部分:普通代码在文件、函数、信任边界和稀有词元上建立倒排索引,然后给每个发现一份简短的候选列表 概率性部分:去重智能体在这份简短列表上进行推理;稳定的跨运行键会重新打开已有记录 |
| 判定(Judgment) | 生产环境可达性与验证 | 单个智能体,通过 MCP 服务器构建关于该漏洞的上下文,了解服务在生产环境中的大致形态。它会搜索 wiki、Jira、git、配置以及所有其他可用来源,试图弄清该漏洞是否真的适用于我们的生产环境,然后据此给漏洞评分。它还会对照源代码验证该漏洞,以判断它在最新的 main 分支上是否仍然存在。 |
| 修复(Fixing) | 生成补丁,运行回归测试 | 在修复前后各运行一次回归测试(过滤到受影响的测试;仅在无法按测试过滤时才运行完整测试套件)。要通过这道关卡,目标测试必须明确地从失败翻转为通过。如果补丁后的测试失败,或全局运行检测到下游回归,提交会被自动阻止,并标记为需要人工介入。 |
表 2:漏洞验证系统(VVS)
去重
用 LLM 把每个发现与其他所有发现逐一比较,复杂度为 O(N^2),在大规模下完全行不通。为了让模型不处于关键路径上,确定性代码会在结构化数据(涉及的文件/函数、信任边界、稀有词元)上建立倒排索引,生成一份简短的真实候选列表。只有到这时,才由智能体查看这份简短列表,判断是否一个修复就能同时关闭其中的若干项。稳定的跨运行键确保重新发现的漏洞会重新打开已有记录,而不是产生新记录。
上下文判定
判定是对幸存下来的发现进行的第二次独立检查。智能体重新核对最新信息,从部署、环境和配置上下文中提取资料,判断该代码路径在生产环境中是否可达,并确定仓库的负责人。这一过程把“当前即可利用”的漏洞,与“真实但处于潜伏状态”以及“真实但被提交到了错误组件”的漏洞区分开来。它把一堆混乱的发现转变为由风险驱动的编排工作流。
自动修复
修复者(Fixer)接收拟议的补丁和单元测试,将其改写以匹配仓库的风格,应用 diff,并运行有针对性的测试。从失败干净地翻转为通过是理想情形,也是唯一可以自动清理的情形;补丁后的测试失败会阻止提交。修复者从不自行合并代码;必须由人工评审该分支。这道关卡是不容商量的人在回路(human-in-the-loop)保障,为变更管理合规提供了干净、牢不可破的追溯链。放任模型自由打补丁,它会很乐意修好一个安全漏洞,同时悄悄破坏某个无关的功能,或者引入几十个新漏洞。
在这三项分诊工作中,每个智能体都被限制在一项狭窄的任务上,外面包裹着确定性的记账代码,而且没有人工对试运行(dry run)签字确认,任何东西都不会写入生产环境。虽然这条流水线把工程瓶颈从发现漏洞转移到了评审和落地修复,但修复者仍然是系统中最年轻、也最慢的部分。
成本
在整个仓库机群上运行数百个智能体并不便宜,但至少开销的构成是可预测的。几乎所有的算力预算都直接投入了猎捕阶段。这使得补缺成为我们在成本与覆盖率之间的调节杠杆,因为每多一轮补缺的成本大约只有最初猎捕的一半。
由于各仓库的成本差异很大,我们按仓库而不是按运行来做预算。我们为每个仓库设置严格的任务上限,并启动一个 50 到 200 个工作进程的工作池。这样你就可以把钱花在真正有发现的仓库上,而不是浪费在没有发现的仓库上。
这也是为什么对我们来说,大规模扫描是定期的积压清扫,而不是针对每个 PR 的检查。对一个复杂仓库的完整扫描可能要花数小时;最长的一次运行耗时超过 14 小时。对于逐 PR 检查这类工作,更便宜、更小的框架才是合适的工具。
如何判断它在起作用
我们通过追踪自动化流水线把刻意制造的工程噪声过滤成高质量、可执行发现的效率,来衡量系统的有效性。因为我们有意调校猎手,让它对那些可以串联成更大攻击的细微原语多报,所以真正的成功指标是:在这堆原始数据到达人类面前之前,我们能多么锐利地对其进行提炼。
为了评估这一点,我们持续追踪有多少原始发现能在每个验证阶段中幸存下来。得益于侦察阶段提供的更好的上下文注入,我们的初始验证拒绝率从 40% 降到了 11%,而高可信度发现的占比从 35% 升至 58%(对应累计约 12,057 条发现)。
下面是撰写本文时,从原始候选项到可执行发现的累计分解。

漏洞发现框架(VDH)
- 原始候选项:发现框架在独立验证之前输出的全部内容。
- 需要复现:看起来合理,但在被信任之前需要人工复现的发现。
- 在验证中被拒绝:验证者推翻了其威胁模型、利用路径、受影响代码或证据。
- 重复项:被合并到同一框架产出的另一个发现上的候选项。
- 通过验证:通过独立验证关卡并进入 VVS 的发现。
- 转往别处的漏洞:被有意路由到此流程之外的发现。
漏洞验证系统(VVS)
- 另一个漏洞发现框架:向同一验证系统输入数据的其他自动化来源。
- 系统中的漏洞总数:摄取之后的合并池。
- 重复项:去重环节认定已被另一个规范发现或工单覆盖的发现。
- 仓库错误 / 其他 / 非风险:噪声类别,包括归属错误的发现、纵深防御项或潜在风险。
- 已发送给团队的漏洞:已最终确定、干净且可供修复的发现。
- 被判定可从互联网利用:现实中的攻击者可以在生产环境中触发的高紧急度发现。
- 未被判定可从互联网利用:紧急度较低但可执行的漏洞(生产问题、依赖风险或配置错误)。
- 最终严重程度划分:用于为工程团队分配优先级的分类方式。
这套框架的核心指标不是一个臆测的召回率分数,而是尽可能把摆在真人面前的未经确认的发现数量压到接近于零。这套架构必须是一个不懈运转的过滤漏斗。
- 在 VDH 生成的 20,799 个原始候选项中,只有约 12,057 个通过了验证。
- 当这些发现被推入 VVS,并与另一个框架的发现合并后,中央池的总量达到 13,841。
- 去重智能体将 5,442 条发现作为重复项合并掉。
- 1,154 条因“仓库错误”或“低风险”被路由到队列中,并在适当情况下被回收到系统里。
- 最终留下 7,245 条可供工程团队处理的可执行发现。
传统的合规规则完全依据静态的 CVSS 分数来规定随意的修复期限(例如“30 天内修复所有高危漏洞”)。我们的上下文判定层把这种合规打勾式流程变成了真正的风险管理。
这套架构能够把发现追溯到其源头,这意味着修复一个根本原因就能解决整簇发现,而不只是逐个修补单独的问题。VDH 系统的性能还通过把仓库划分为(区域 × 攻击类别)单元格来衡量,并迭代运行补缺智能体,直到它不再产出发现。每当我们更新底层提示词时,都会用一个留出的仓库进行测试,看总的覆盖单元格数量是否真的有变化。
框架接入了自动化的健康信号,以便在流水线早期捕获系统故障。如果一次猎捕结束得可疑地快,并且没有派生出子猎捕或补缺任务,通常说明是某个依赖崩溃了,而不是代码库真的很干净。为此,系统会把任何以零发现结束的猎手智能体标记为“过浅”,并立即将其重新排队、再次运行。
最后,我们前面描述的独立分诊环节进一步强化了系统的稳健性。通过用不同的模型和独立的逻辑权重重新评判所有提交,我们确保了无偏见的对抗性验证,使其与用于发现的具体模型解耦,从而提供了一个无论使用哪个模型都始终存在的信任层。
这一切都还没有完成。我们在不断改动系统,它离完美的科学还差得很远。但原始的候选发现现在已经很廉价了,唯一值得做的工作,是把它们变成可靠、可验证的代码修复。
构建你自己的框架,意味着要接受 AI 模型是多变的,但你的编排层不必如此。通过将安全逻辑与任何单一供应商解耦、强制对抗性验证并自动化分诊流水线,你可以把堆积如山的 LLM 噪声变成可靠的、覆盖整个机群的防御引擎。
我们的“北极星”指标:衡量真实世界中的速度
每个代码库都略有不同,因此为了向你展示它在现实中究竟如何运作,我们基于一次标准的仓库运行绘制了一个贴近现实的基准。请注意,这代表的是对一个仓库的单次运行;随着覆盖整个机群的持续循环不断去重、过滤和回收发现,终身候选项的数量会减少约 65%。
通过自动打补丁节省的工程工时:我们不关注静态基线,而是通过技术吞吐量、处理速度,以及消除人工分诊瓶颈的能力来衡量流水线的健康状况:
- 初始验证筛选:对于一个标准仓库(约 3 万行代码),这会产生 100 条初始发现,完整运行需要 3 到 4 小时,全程保持高度聚焦的上下文窗口。
- 压缩:去重层和上下文判定层并行处理这些候选项。在 3 小时内,系统把这批发现从约 100 个原始候选项压缩并提炼为 80 个互不相同的高保真漏洞。
- 修复:自动化的修复者平均每个漏洞花 5 分钟处理这 80 个不同的漏洞。总体而言,系统可以在大约 14 小时内完成发现、验证、去重并提交可用的拉取请求。
缩短严重缺陷的平均解决时间:当然,你不可能把 80 个补丁一次性全部推上生产环境而不出问题。为了保证部署安全,我们的系统采用分层发布:
- 关键暴露遏制:系统会把严重、高危且可被利用的漏洞(平均 80 个中有 10 个)单独挑出来。我们对它们走快速通道进行人工评审,并纳入发布周期,使其在 5 天内在生产环境中完全修复。
- 渐进式加固:其余的潜在风险、轻微配置异常和较低紧急度的漏洞,会在 15 到 20 天的窗口内逐步推入生产环境,以保证平台稳定。
我们如何处理这些补丁
这些发现来自一项隔离的、划定边界的研究实验,目的是对我们的代码进行压力测试。它们并不代表我们线上生产环境中真实存在、尚未修补的漏洞。
由于这套框架在我们的测试环境中持续运行,等你读到这里时,这些具体数字已经完全过时了。流水线发现的每一个漏洞都附带了一个可运行的测试用例来演示该漏洞,以及一份补丁草稿。我们的安全团队正在系统地处理这些报告并应用必要的修复,这意味着你每天使用的 Cloudflare 产品已经针对这些攻击向量得到了积极的加固。
随本文一同发布的,还有我们用来开发这套框架的最初那个技能。发布前我们做了少量整理,使它更易于理解和集成,但技能本身基本保持不变。希望框架本身也能很快跟进。它可以成为你构建自己的漏洞发现框架、你自己的技能,或任何最适合你的东西的起点:
github.com/cloudflare/security-audit-skill
如果你的团队也在处理同样的问题并希望交流心得,欢迎通过 security-ai-research@cloudflare.com 联系我们。
