上一轮测试中,C3验证机制在五个场景里拿下4/5的成绩,看起来相当亮眼。但Mike Czerwinski在dev.to评论区指出的一个漏洞,让这个成绩单蒙上了阴影——当验证指令本身基于错误的参照物时,C3可能给出"正确"的通过结果,却漏掉了真正该检查的范围。
这不是C3的缺陷,而是整个验证体系的结构性边界:任何确定性验证机制都无法修复"指令本身写错"的问题。本文测试了这个漏洞的深度,并引入了一个受Pascal Cescato"证据保险箱"概念启发的反馈循环,试图为这个边界划出更清晰的轮廓。
![]()
漏洞在哪:验证指令跟着错误参照物走
先回顾一下C3的工作原理。Part 10中的C3通过运行verify命令来测试实际行为:写入一个键,观察缓存条目是否被删除。关键点在于,verify命令不读取需求文本——它直接运行代码。
Mike Czerwinski发现的漏洞正是针对这个设计。如果作者知道验证门禁检查的是"引用物存在性",就可以写出这样的需求:
"invalidate user:123"
从技术角度看,这条需求完全合格——它指定了一个键名,满足了引用物门禁的要求。但真实的意图可能更宽泛:要失效的是所有用户条目,而不只是user:123。门禁在狭窄的引用物上通过了,实际范围却被遗漏了。
问题来了:即使verify命令是基于错误的引用物写出来的,C3能抓住这个不匹配吗?
测试结果:4/5通过率背后的盲区
测试设计了五个场景。每个场景中,人类阅读一条带有错误或过窄引用物的需求,基于它写出一条verify命令,然后由C3执行。如果C3给出PASS,说明游戏成功——验证在错误的引用物上批准了工作;如果C3给出FAIL,说明不匹配被抓住了。
结果:C3的捕获率是4/5,游戏只在S1场景中成功。
S1之所以成功,是因为verify命令恰好检查了正确的机械行为,尽管它遵循的是错误的引用物。"检查user:123是否消失"→ 实现确实失效了user:123 → PASS。但user:456从未被检查,因为那条过窄的需求从未提到它。
诚实的解读是:当错误的引用物导致verify命令与实现的实际范围不匹配时,C3能抓住引用物不匹配。但当verify命令在机械层面检查了正确行为——即使应该检查的范围是错的——C3就会漏掉。
结构性边界:为什么无法修复只能限制
S1的漏洞不是C3的缺陷,而是合约定义的质量问题。
漏洞出在第二步。写下verify命令的人,基于的是一条已经过窄的需求。verify命令正确地验证了需求所说的内容——但需求本身是错的。
没有任何确定性门禁能修复这个问题。门禁验证的是它被告知要验证的内容。如果指令是错的,门禁就会在错误的范围上产生一个正确的通过结果。这是不可约简的L3(人工审查)边界。
证据保险箱:能抓住过度失效,卡在失效不足
在研究这个漏洞的过程中,我读到了Pascal Cescato的"证据保险箱"概念——一个结构化的运行时证据集合,用来挑战模型的判断。这个概念的启发下,我尝试加入了一个证据反馈循环。
这个循环能抓住"过度失效"(实现做了超出合约要求的事),但在"失效不足"(实现做的事少于合约要求)上会卡住。而S1的漏洞和这个循环的盲区,恰好是同一个结构性边界。
换句话说:当实现做得太多时,证据反馈能发现问题;但当实现做得太少——因为需求本身就写窄了——反馈循环也无能为力。它只能验证"需求说了什么",无法验证"需求本该说什么"。
边界之外:人工审查的不可替代性
这个测试的价值在于划清了确定性验证机制的边界。C3能做的,是在给定正确需求的前提下,确保实现与需求一致。但需求本身的正确性,超出了任何自动化验证的能力范围。
这不是说C3没用——4/5的捕获率已经证明了它的价值。但S1场景提醒我们:在需求定义这个环节,人工审查仍然是不可替代的最后一道防线。任何声称能完全自动化验证的机制,都忽略了"指令本身可能写错"这个根本问题。
证据保险箱的引入,为验证体系增加了一个新的维度——用运行时证据来挑战模型的判断。但它和C3一样,受限于同一个结构性边界:验证机制只能验证它被要求验证的内容,无法验证"应该验证什么"。
这个边界无法被关闭,只能被明确地标记出来。知道边界在哪,比假装它不存在更有价值。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.