探索性案例研究 · ep1-f005
原题通过之后,
漏洞是否真的修好了?
以一个真实的凭据泄漏缺陷为例,观察不同模型驱动同一 Hermes Agent 完成修复后,补丁能否覆盖客户端复制这一额外边界。
- 研究问题
- 通过原始回归测试,是否足以说明修复满足更完整的凭据隔离要求?模型回答中识别出的风险,是否落实到了代码?
- 方法
- 9 个模型各运行两次,主实验预算 420 秒;另对 GLM-5.3 做两次 1200 秒探针。随后对归档构造函数、未修复基线和上游参考函数实施统一 HTTP 边界检查。
- 主要发现
- 解释范围
- 本报告比较的是指定任务、提示、Agent、工具与预算下的系统运行。单任务、小样本;不估计模型总体成功率,也不建立能力排名。
01 · 任务背景
正确行为:请求只能携带选定的凭据
给第三方供应商设置 API key 后,SDK 仍会从环境读取另一份 Bearer token,导致同一请求携带两套凭据。任务要求阻止多余凭据被发送,同时保留合法鉴权。上游采纳的方案是参考实现;允许行为等价的其他实现。
| 场景 | 应保留 | 应排除 | 本报告如何检查 |
|---|---|---|---|
| API key 原始请求 | 指定的 X-Api-Key | 环境中的 Bearer token | 原题回归测试 + 函数补测 |
| Bearer 原始请求 | 指定的 Authorization | 环境中的 API key | 函数补测;基线已通过 |
| 复制客户端后的两种请求 | 与原始请求相同 | 复制时重新带入的环境凭据 | 实验后补测;非原题评分项 |
展开:问题时间线与上游参考答案
0genlab 发现并复现凭据跨厂商泄漏
显式配置了第三方 API key,SDK 仍把环境里的 Anthropic Bearer 凭据附加到请求中。用本地 HTTP 服务复现并提交 issue。
问题 #105774 ↗上游最终合入双向 Omit 方案
kshitijk4poor 接续原 PR,通过 sdk.Omit() 删除未选定的鉴权头,并保持客户端复制后的保护。合并提交 036a20b3。
最终方案 #107978 ↗
上游 PR 在 SDK 0.87.0 上验证;本报告的补充检查统一使用冻结镜像的 SDK 0.94.0。
02 · 研究设计
三层证据,分别回答三个问题
| 证据层 | 问题 | 判据与用途 |
|---|---|---|
| 原题验收 | 是否完成当时可执行的验收? | 恢复原始 regression_test.py 后判分;只有 API key 原始请求这一项回归测试。任务文字另要求保留 OAuth,但该测试未覆盖它。 |
| 修复稳健性 | 归档函数能否满足四项边界检查? | 两种鉴权 × 原始/复制请求;统一 SDK 与本地 HTTP。属于事后探索,不改写历史 score。 |
| 回答证据 | 答复如何描述风险与验证? | 按统一规则阅读可见内容;“说到、计划、声称验证、实现保护”分别记录,不合成全面性分数。 |
实验条件与证据完整性
| 项目 | 本次记录 | 可比性与限制 |
|---|---|---|
| 任务与提示 | 相同旧源码、TASK.md、原始测试 | 已给根因、文件位置与 None 陷阱;评估实施修复,不是独立发现根因。未给上游补丁和复制验收。 |
| 样本与纳入规则 | 固定两份快照,18 次主实验 + 2 次探针 | 保留全部超时;按 task 排除 1 次 p1 冒烟测试。较早校准运行不属于本队列。 |
| 模型与渠道 | 9 个归档模型标识,经 AIHubMix 接入 | 模型名称不是可重建权重版本;运行跨日。上游服务变化未被完全控制。 |
| Agent 与环境 | 相同镜像、独立容器、2 CPU / 4 GB、固定 SDK | Agent 与任务源码分别锁定。宿主判分依赖未完整冻结。 |
| 预算 | 420 秒主实验;GLM 单独 1200 秒组;max_turns=20 | 延时组为新运行,非续跑;不与主实验混合估计成功率。 |
| 采样与推理配置 | 本报告尚未逐请求汇总 temperature、seed、推理预算等 | 标为待核对;不能仅凭同一 Agent 配置断言各上游的有效参数一致。 |
| 运行安排 | 实际时间由快照保留 | 未建立随机执行顺序或时间分块设计;平台负载、重试等影响未独立控制。 |
| 补测与文本审阅 | 函数级补测;单次 AI 辅助文本审阅,无独立第二审阅者,未逐项复核回答中的工具执行声明。 |
展开:版本、资源、网络与复现限制
展开:指标定义、判分实现与数据来源
- score / exit
- score=1 表示原始回归测试通过;exit=0 表示 Agent 正常结束,exit=124 表示外层超时。两者不是同一判据。
- 测试修改标记
- score.sh 在宿主恢复原始测试再执行;test_modified_by_model 独立记录,不自动扣分。本期20次均为 false。
- wall_s
- 从运行启动前到容器结束、日志收集及移除后计时,不含后续判分。
- API 调用数
- official 取 Hermes api_calls;wire 只计带有效 usage 的实录响应,未必覆盖全部失败和重试。不等于 Agent 主循环轮次。
- 输入口径
- wire 的未缓存输入 = prompt_tokens − cached_tokens;official 使用导出 input_tokens,缓存读取另列。列表均值为两次算术平均。
- 补测范围
- 归档构造函数 + anthropic 0.94.0 + 容器内回环 HTTP 服务;容器无外网,不调用模型。补测日期:。
03 · 结果
通过原题的补丁,仍全部漏掉复制边界
图 1 · 每次运行的原题结果与补测结果
模型按名称排列 · 点击运行编号并排核对证据| 实现 / 运行 | 原题结果 | API key 补测 | Bearer 补测 | ||
|---|---|---|---|---|---|
| 原始请求 | 复制请求 | 原始请求 | 复制请求 | ||
图 2 · 相同预算下的两次运行耗时
每个点是一场主实验;同一模型的两次运行上下分开。● / ○ 为通过,■ 为超时。模型按名称排列;右侧为两次原题通过数。
补充观察:GLM 延时探针(独立两次运行)
B · 单独延时探针 / glm-5.3
增加预算后,结果从未通过变为通过
附图 A · 两组独立运行固定同一镜像和资源,把外层预算从 420 秒改为 1200 秒,重新运行两次。下图是同一时间刻度;虚线表示各组预算。
04 · 机制解释与定性审阅
保护停在客户端实例上,复制后没有延续
图 3 · 从构造到复制,保护在哪一步失效?
| 实现方式 | ① 构造客户端 | ② 原始请求 | ③ 复制客户端 | ④ 复制后的请求 |
|---|---|---|---|---|
| 未修复基线 | SDK 读取环境 token | 带出额外凭据 × | 再次构造,继续读取环境 | 仍带出额外凭据 × |
| 构造后清空属性 | 读取环境 → 清空 auth_token | 当前实例不带额外凭据 ✓ | None 触发 SDK 再次读取环境 | 额外凭据重新出现 × |
| 临时移除环境变量 | 移除变量 → 构造 → 恢复变量 | 当前实例不带额外凭据 ✓ | 从已恢复的环境读取 token | 额外凭据重新出现 × |
| 上游请求头排除 参考函数 | 保存显式排除头的规则 | 构建请求时由 Omit 删除多余头 ✓ | 复制保留 default_headers 规则 | 仍在请求头层排除 ✓ |
回答是否识别了复制风险?逐次使用同一套规则
145 条非空可见答复,包含过程说明与最终答复;另排除 21 条辅助标题生成回复。不读取 reasoning_content,不把工具参数或工具输出当作模型回答。以下是单次 AI 辅助定性审阅,尚无独立复核。
审阅规则与判定边界(版本 1.0)
所有 20 次运行使用相同审阅范围。“未见明确表述”只描述已保存的可见内容,不能推断模型没有考虑。提及 Omit 只记为排查意图,不记为完成方案。复述任务已给出的根因与 None 陷阱,不算独立发现。
展开:全部 20 次运行的统一文本审阅表
| 模型 / 运行 | 复制边界表述 | Omit 表述 | 最终交付声明 | 独立结果:原题 / 复制 |
|---|
三个代表性片段:识别风险、限定范围、提及机制
05 · 讨论与局限
这次实验支持什么,还不能支持什么?
| 本次证据支持 | 不能据此外推 | 下一步所需证据 |
|---|---|---|
| 16 份原题通过实现均未通过复制补测 | “这些模型普遍不能修安全漏洞” | 多个独立任务、更多重复、预先确定的任务抽样规则 |
| GLM r14 明确表述复制重读环境风险,但该次未完成 | “GLM 思考最全面”或“说到风险就代表会解决” | 统一审阅规则的独立复核,结合工具执行与代码落实 |
| GLM 延时组另外两次通过原题 | 预算增加是结果变化的唯一原因 | 受控预算对照、执行顺序控制及更多重复 |
| 在指定 SDK 下,参考函数四项检查均通过 | 参考方案已通过完整安全审计 | 完整调用路径、并发、多个版本与其他运行环境检查 |
效度限制:任务已提供根因;模型调用跨日且版本未完全固定;单模型主实验仅两次;补测为事后探索;复制检查覆盖函数而非完整 Agent;文本审阅者知道实验结果,存在解释偏差。以上限制保留在结论附近,不藏在页尾。
06 · 开发者行动与证据
把这次发现转成验收要求
- 从一次请求扩展到客户端生命周期:验收同时检查原始实例和复制实例;两种鉴权均保留指定凭据、排除额外凭据。
- 让完成声明对应实际检查:报告列出执行过的场景,未测项明确保留,不用“已修复”替代验证范围。
- 下一轮先固定规则再运行:若把复制场景升级为正式评分项,应建立新任务版本、新队列与新结果,不回写本次成绩。
附录 A · 全部 20 次运行的数值明细
| 模型 / 运行 | 预算 | 耗时 | score | exit | API 调用 / 来源 | 测试被改 |
|---|
附录 B · 输入与缓存用量
B · 执行开销 / 仅 420 秒主实验
总输入里,有多少来自缓存?
附图 B · 每模型两次算术均值把未缓存输入与缓存读取分开展示。超时运行只累计停止前的已记录用量;数据来源与覆盖范围详见口径说明,不能直接据此比较费用。
附录 C · 其他问题与工具链记录(7 条)
补充记录
实验过程中还发现了什么?
7 条记录 · 按需展开包括真实漏洞、早期校准问题和工具链观察;不同阶段分开标注,不把历史污染运行计入本期结果。