CounterProof

独立的对抗性代码审查

本页由 AI 从英文翻译,尚未经过母语人士审校;如有出入,以英文原文为准。 阅读英文原文

对抗性代码审查:这个术语遗漏了什么

如今有四种截然不同的做法都叫这个名字。它们都以攻击者的眼光看待你的代码。没有一种能单独同时给你一份来自你组织之外审查者的签名报告,以及一份清楚说明每个发现项被证明到什么程度的陈述。下文说明:我们交付什么,第二个 AI 模型不能交付什么,以及我们在你购买之前说明的局限。

对抗性代码审查,是带着攻击者的问题(怎样才能让这段代码做它不该做的事)去阅读源代码,而不是带着维护者的问题,即代码是否正确、整洁。这种视角会改变审查者看的地方。但它本身并不能表明一个疑似缺陷是真实的,不能表明两位审查者没有犯同一个错误,也不能表明谁要为结论负责。

我们以这个名称交付的,是一份签名的发现项记录。每个已确认的发现项都按其实际达到的证据定级,每一处经过检查而未发现问题的部分,都连同检查它的方法一起记录下来。审查者独立于你的代码的作者,并由一位具名人员裁定发现项,因此你的收购方、保险公司或客户可以引用这一结果。一个发现项如果经不起检验,会以书面形式撤回。本页说明,与这个说法如今的其他含义相比,这些增加了什么,以及代价是什么。

这个说法如今的含义

在我们对市场的解读中(2026 年 9 月的一次检索,而非全面普查),有四种做法使用这个名称。每一种都做了实事。前两种是我们自己也在使用的工具。

批评者提示(critic prompt)。一个智能体编写一项变更;另一个智能体在全新的上下文中,而且往往来自另一个模型家族,以带有敌意的视角阅读这个差异。这消除了自我评分,在适用的地方,这一点很重要。在八个评判模型上测得的结果是:自我偏好在一个模型上很大,在两个模型上中等,在两个模型上接近于零,在三个模型上略有反转(自我偏好偏差[英文],arXiv:2410.21819)。作为一种做法,它提供了攻击者的视角。它本身不提供留存的记录,不测量两次阅读的相关程度,也不提供签名。

双审查者编排器。两个模型家族独立审查同一项变更,有时彼此看不到对方的结果,然后相互批评。其中一个汇总结果,并且往往由它实施修复。盲审阶段是对的。但这种形态让审查模型既做自己发现项的裁判,又做它们的修理工,而且它本身既不产生基准事实,也不产生相关性的测量。

平台集成(ensemble)。一个审查产品在自己的流水线内运行多个模型,返回带有严重程度的拉取请求评论。它带上这个名字,主要是沾了边:用这个说法自称的,绝大多数是前两种做法。在这里,使用多个模型是供应商的设计决定;按其销售形态,这种形态不会公开哪个模型说了什么、依据什么证据,或者它们在出错时一致的频率有多高。它提供的是数量和集成。一串评论不是审计。

具有攻击者思维的人工审查者。具有攻击方背景的读者以攻击者的方式审查源代码,无论是作为一家公司还是以众包的形式。这是没人设想过的攻击被发明出来的地方,也是我们的做法要为之提供支持、而非取而代之的做法。作为一种形态,它本身并不把证据和裁决分开,而且其审查者彼此之间的独立性没有测量过,所有人的都没有。

三种属性,一个词

“adversarial”(对抗性)一词捆绑了三种属性:任务视角(攻击者的问题)、审查者之间的错误独立性,以及签名者的组织独立性。一次审查可以具备其中一种属性,而不确立其他属性。

做法任务视角错误独立性组织独立性
批评者提示是本身不具备:一份任务说明,一个评判者否
双审查者编排器是有盲审阶段,未经测量;由审查者仲裁否
平台集成是供应商内部,不公开否,工具不能签名
人工审查者是一个团队是
CounterProof是有约束、有记录,未经测量:不同的模型家族,任务说明存档,具有相关性或失败的 AI 审查者不予计数是:独立于代码作者的具名签名人;行业利益以书面形式披露

一个由你选择、指向你给它看的内容、由你评判的第二个模型,不是对手。它是从你本来就身处其中的同一个分布里抽出的第二个样本。由此产生三个缺口,没有一个是模型质量的问题。

缺口一:不同的供应商不等于独立的见证者

两家不同公司的模型有不同的训练数据,所以一致就是佐证,沉默就是可以放心;这就是人们的推理。

一项涵盖 350 多个模型的研究发现,当两个模型都答错时,它们给出同一个错误答案的频率远高于随机水平:在一个排行榜上平均为 60%,在另一个上为 42%。来自不同提供商的模型组合自始至终都包括在内。该研究关于跨提供商的陈述,是从其回归模型得出的推断,而不是仅针对跨提供商组合的分析(Kim 等,ICML 2025,arXiv:2506.07962)。

它测量的是基准测试和简历筛选,而不是漏洞审查,因此它没有给出任何关于代码的重合数字。它确立的是,不同的供应商并不会自动相互独立。认为这应当改变你看待第二位审查者在你的代码上表示同意的方式,是我们的推论。

文本学者在五个世纪前就把同样的观点定成了一条规则:一份被证明源自另一份现存抄本的抄本,会被剔除出考量,不是因为它错了,而是因为它没有增加任何证词。

人们忽略的相关性渠道不是供应商,而是任务说明:提示词、工具、证据包和角色设定,会让不同供应商的审查者产生相关性,而两个拿到同一份精选材料包的模型,会共享这份材料包遗漏的一切。我们把任务说明视为审查设置的一部分,并与审查一起存档。这是针对一个可能的依赖来源的预防措施,并不是对其大小的测量,也不能证明我们的程序消除了它。《攻击者可以运行任何模型》(英文)详细阐述了这一点能支持什么、不能支持什么。

缺口二:没人决定什么时候不该征求意见

有些主张有一个判定器:某种无需任何人的意见就能回答它们的东西。“这条路径会执行吗?”运行它。“规范允许这个值吗?”去读规范。只要存在判定器,一项运行过的测试的分量,就高于一个进行过推理的模型,也高于一个同样进行过推理的人。

被问到一个正则表达式是否匹配某个路径时,审查智能体会就这个正则表达式是否匹配这个路径给出一个流畅的意见。把这个问题改交给一次执行,是一个工程上的选择;在我们的解读中,大多数审查工具都没有作出这个选择,因为这一类产品是按每个拉取请求的发现项数量来购买的,而一个最后变成两分钟脚本的发现项,就不算发现项了。我们从内部了解这个难处:我们曾构建一项检查来强制执行这种改交,交付时它的自检已经定义好,却从未被调用。《什么能判定一项发现》(英文)就是这次事故的剖析。

没有判定器的主张(有多严重,这个信任假设能否接受,攻击者会不会费这个劲)属于判断,再多的模型输出也无法把判断变成事实。

缺口三:没有人签名

一次内部的智能体运行(无论用多少个模型,无论它们的上下文隔离得多好),产生的都是你自己关于你自己代码的说法,你的组织之外没有人为其中任何一句话负责。根据我们的经验,这正是收购方的律师、网络保险公司的核保人和企业客户的安全团队首先要求的属性,因为这是你的团队无法为自己的工作提供的属性。

根据《网络弹性法案》,大多数制造商可以进行自我评估,然后要为其背后的每一份记录负责:技术文档中所开展测试的报告(附件七第 6 点),作为履行测试义务的证明(附件一第二部分第 3 点),在产品投放市场后至少十年内,或在支持期内(以较长者为准),供市场监督管理机构随时调阅(第 13 条第 13 款)。针对被主动利用的漏洞的报告义务已于 2026 年 9 月 11 日开始;该条例自 2027 年 12 月 11 日起全面适用。外部评估支持你汇编的档案;它不是档案本身,而且我们不是公告机构。《标准可能推迟,报告日期不会》(英文)介绍了时间表。

你从我们这里得到什么

以下没有一项是秘密。一个运作良好的团队可以采用其中任何一项。你购买的是:所有这些都已做到、都已写下并已签名,形成一份你可以交给不在场之人的记录。

  1. 判定器优先。一项可判定的主张,在就其征求任何意见之前,先被交给执行、编译或规范。我们的审查包工具在缺少证据包时拒绝生成任务说明,而且任务说明会写明该证据包无法显示什么,因此,一位审查者对它从未拿到的内容保持沉默,会被解读为沉默,而不是没有缺陷。

  2. 审查者来自不同的模型家族,任务说明存档。有文件访问权限的审查者从源代码构建自己的证据;没有访问权限的审查者只读任务说明所携带的内容。每位审查者收到的任务说明、工具和证据包都是记录的一部分。没有返回可用输出的审查者记为“缺席”,绝不记为同意;被发现与另一位审查者共享上下文的审查者会被标记且不予计数,包括有一次我们的一位审查者被发现其上下文中有我们自己的发现项文档。

  3. 盲审输出在裁定开始之前就已记录。每位审查者逐字说了什么,连同其运行细节(模型、运行它的软件、日期、打开的文件),都在任何人进行比较之前写下来。

  4. 计票永远不能确立任何事情。发现项不通过投票关闭。审查者对代码做什么有分歧时,由源代码决定;当一项否决理由只是“不确定”时,这项主张会进入一个留存队列,有负责人、有截止期限、有重新开启的标准,并重新交给一个尚未审查过它的模型家族。

    确认沿着证据梯级向上推进:源码追踪、编译证明、测试、实际复现;对于任何概率性主张,则是一个附有区间的比率,并保留每次试验的日志。未达到任何梯级的,属于仅看似成立,这是另一种处置结果,而不是更弱的梯级。一处经过检查而未发现问题的部分,会连同检查它的方法,以及表明该方法能够找到其所寻找之物的对照,一起记录下来。

  5. 由一位具名人员签名,并以书面形式撤回。在一个你的维护者和收购方都可以引用的标识符下,包括发现项是我们自己的时候(英文)。我们公布自己的检测手段失灵的案例,因为一家无法告诉你它错在哪里的审查机构,在它说自己对的地方也没有立足之地。

用多个模型投票并取多数,是一种集成;按其销售形态,它不向买方公开上述任何内容。我们在 2026 年 9 月检索时,没有找到任何一家机构把这套做法作为买方可以检查的协议来提供。这是关于形态的陈述,而不是关于发现率的陈述:我们在你购买之前告诉你的内容(见下文)说的正是这一点。

这在你的漏洞管理中处于什么位置

发现问题不是我们赚取费用的地方。扫描器、模糊测试工具、审查平台和你自己的工程师,产生候选发现项的速度已经快过任何人裁定它们的速度,我们的委托也会发现问题;那是副产品。我们获得报酬的工作,位于生命周期断裂的地方:

  • 裁定。一个候选发现项先被交给它的判定器,再按其达到的证据梯级定级,因此严重程度跟随的是证明,而不是模型的自信。
  • 关闭。分歧由源代码解决,而不是由投票解决;“不确定”被放在一个有负责人的留存队列中,而不是悄悄变成“已接受”。
  • 保证。结果是一份由你组织之外的一位具名人员签名、并且可以由其撤回的记录,这是技术档案、尽职调查材料包或核保申请中,你无法就自己的工作自行出具的那一部分。

如果把我们放在识别阶段,我们就要在数量上与你的扫描器竞争,那是我们不参加的比赛。把我们放在一个发现项变成一项日后需要有人为之辩护的决定的地方,记录就是产品。《发现项不是裁决》(英文)介绍第一个阶段,《什么能判定一项发现》(英文)介绍第二个阶段中判定的那一半;留存队列见上文。《〈网络弹性法案〉下的漏洞管理》(英文)逐一介绍每个阶段,以及《网络弹性法案》在每个阶段规定的义务。

在你购买之前我们告诉你的

这些是为了保护你,所以写在页面上,而不是写在合同里。

  • 我们出售的是记录,而不是发现率。我们没有证明一个审查小组比一位优秀的审查者能找到更多真实缺陷,我们也不这样主张。我们把完整的多模型审查与一次廉价的单次审查进行了比较,事先登记、盲评打分,结果它在两个代码库上都没有完全胜出:在一个代码库上它输了,在另一个代码库上,它只是靠删除了审计员判定为真实的发现项,才达到了自己的精度触发条件。《我们测量了自己的方法,它没有挣回它的成本》(英文)。我们依据的案例报告说,一个更简单的原则同样符合这些证据:凡是你会据以采取行动的东西,都做一次独立的外部审查;争议通过阅读源代码解决;不交付任何未经测量的数字。如果你从本页只记住一点,就记住这一点。我们出售的,是做过这些事的记录,并附有签名。我们的 AI 审查者之间的独立性受程序约束并有记录,但没有针对代码审查进行测量;没有人测量过。记录中的每个发现项,要么达到了一个具名的梯级,要么被标明没有达到。
  • 作者本人也在流程之中时。我们的规则是:提出一项有争议主张的人,不能单独裁定它。我们的审查团队只有两个人,这意味着接的委托更少,并且会拒绝我们领域之外的工作。这也意味着这条规则目前还没有强制执行它的机制;我们自己的案例报告把这一缺失列为其最大的未修复弱点。在实践中,一项有争议的主张在关闭之前,会先交给源代码,并交给一个尚未审查过它的模型家族;裁定会被记录,签名人具名,因此你可以看到哪些发现项是这样关闭的,并自己权衡它们。
  • 没有任何方法能找到没有见证者提出的缺陷。校勘,即把审查者的输出相互比对,只能在已经提出的内容中进行选择。测试、模糊测试、证明和专家的阅读,是通向同一目的地的不同途径,一个严肃的安全计划也会使用它们。
  • 我们独立于你的代码的作者,但并非在每个行业都毫无利益。CounterProof 隶属于一个开发支付和数字资产托管基础设施的集团。如果你在这些市场中开展业务,我们的关联方可能与你相邻,或与你竞争。在任何委托开始之前、在任何资金往来之前,我们会以书面形式告诉你集团开发什么、在哪里运营。对于由 CounterProof 或我们集团内任何公司编写的代码,我们不出具任何评估。

更完整的论证及其自身的局限,见我们的工作论文,这是一份预印本,未经同行评审:research.counterproof.io(英文)(doi:10.5281/zenodo.22030516)。

我们常被问到的问题

对抗性代码审查和渗透测试是一回事吗? 不是。渗透测试检验的是一个正在运行的系统;对抗性代码审查是在锁定版本上阅读源代码,并对照代码确立发现项。两者都具有攻击者思维;它们回答的是不同的问题,一个严肃的安全计划会两者并用。

我就不能用两个 AI 模型检查一遍自己的代码吗? 可以,第二位审查者也许能提出第一位没有提出的候选发现项。它无法产生的是组织上的独立性:你的团队选择审查者看到什么、设定问题、评判答案,而你的团队之外没有任何人签名。

一个审查平台已经在运行多个模型。那不就是一个审查小组吗? 不是。一个由供应商选择模型、你看不到单个模型输出、分歧在流水线内部解决的集成,是一个由多个部件组成的检测手段。一个审查小组是多个检测手段,它们的输出被分别记录,它们的分歧由源代码解决。

对抗性代码审查只适用于 AI 生成的代码吗? 不是。这种方法不在乎是谁(或什么)编写了代码。机器编写的代码让这个缺口更难被忽视,因为数量增加了,而流畅掩盖了其中的猜测。

这和 AI 代码审查工具有什么不同? 那些工具产生发现项,我们也把这一类工具当作检测手段来使用。工具做不到的,是承担责任或签名。

更正

更正,2026 年 10 月 7 日。结尾的推论清单曾说第二位审查者在代码上的同意“应当打折扣”,这可能被理解为“不予理会”,说得比上文得出的推论更多;现在它陈述的是同一个推论。本页曾说自我评估背后的记录“保存十年”;根据第 13 条第 13 款,这些记录应在产品投放市场后至少十年内,或在支持期内(以较长者为准),供市场监督管理机构随时调阅,本页现在如此表述。指向我们时间表笔记的链接曾把其标题写作“The standards slipped, the deadline did not”,这不是它的标题,而且把“可能推迟”变成了“已经推迟”;现在给出的是它的真实标题“The standards may slip. The reporting date does not.”。我们还对措辞、标点和链接作了较小的修改。这些问题是在把本笔记翻译成荷兰语、并对照条例和资料来源核对时发现的。


上文标为推论的内容,以及这里的推论:相关性错误应当改变你看待第二位审查者在代码上表示同意的方式(Kim 等测量的是其他领域);任务说明会让审查者产生相关性;这四种做法构成了市场的形态,以及审查工具在每个拉取请求的发现项数量上竞争(我们的解读,一次检索,2026 年 9 月);组织独立性是收购方、保险公司和企业买方首先要求的东西(我们的经验,而非调查)。如果本页有误,我们会在本页以书面形式更正。