English | 简体中文
跑分前审计 Agent 评测沙箱,把被测模型可达的真实凭据换成 mock 网关
Harbor/Pier 这类 Coding Agent 评测沙箱默认可出外网(no-network/allowlist 隔离需要显式配置),而跑分配置又必须带上 OpenRouter、ModelScope 的网关 endpoint 和 key。这些 key 经 [environment.env] 注入后就在被测模型可达范围里——公开观测中,DeepSeek V4.1 Flash 已经主动发现并滥用过这一暴露面。BenchGate 在评测启动前给出凭据可达性账本(audit),并用本地同构 mock 网关替换(gate)把"改配置"变成"可复跑的 CI 门禁"。
三个事实叠加,让"跑分前审计沙箱凭据"成为必要步骤:
- 沙箱默认不隔离,凭据默认在场。 Harbor 的
network_mode默认public,no-network/allowlist需要显式配置;Pier 的allow_internet默认true。同时评测必须可达推理网关,OpenRouter/ModelScope 的 endpoint 和 key 通常就写在[environment.env]里,由 harness 在运行时解析进 agent 进程环境。两者组合意味着:默认情况下,被测模型拿得到能消费的真实 key。 - 模型会找到它。 r/LocalLLaMA 的 PSA 报告并经我们复核(Harbor
harbor-framework/harbor、Pierdatacurve-ai/pier上游源码逐行验证,见 docs/evidence/repro.md):DeepSeek V4.1 Flash 在 Harbor/Pier 沙箱里主动发现并滥用了这些可达凭据;同代的 V4、GLM 5.3+Flash、Qwen 未观测到类似行为。哪次评测碰到哪种行为不可预知——这正是不该由人肉保证的部分。 - 事后工具救不回分数。 gitleaks 一类扫的是仓库代码,OpenRouter 花费告警响起来时 token 已经烧了、这次跑分已经作废。缺的是一个在评测启动前执行、可重复、以 exit code 说话的门禁。
断网跑评测不解决问题:SWE-bench 式评测必须可达推理网关。正确的做法是保留网关可达性,但把真实凭据换掉——这就是 gate 做的事。
# 从源码(Go 1.24+)
go install github.com/SuperMarioYL/benchgate/cmd/benchgate@v0.1.0
# 或下载 release 二进制(linux/darwin × amd64/arm64,含 checksums.txt)
# https://github.com/SuperMarioYL/benchgate/releases单二进制,无守护进程;audit 不需要 Docker,唯一的外部请求是一次对网关 /models 的无鉴权 GET(可用 --offline 关闭)。
仓库自带两个示例配置:benchgate.example.yaml(带着真实形状的 fixture key,不是可用凭据)和它的清理版 examples/clean.yaml。
$ benchgate audit benchgate.example.yaml
benchgate 0.1.0 — credential exposure ledger
config: benchgate.example.yaml (job_yaml)
network: public
findings: 2 (reachable: 2)
[1] MODELSCOPE_SDK_TOKEN
surface=env gateway=modelscope reachable=true
detected=value_shape resolved=true
fingerprint: ms-…(len=39) sha256:0b08e5ab4b6dcd18
- policy: network_mode "public": no egress control is configured, so the gateway endpoint is reachable and the credential is spendable
- http_no_auth: GET https://api-inference.modelscope.cn/v1/models -> HTTP 200 (no auth header sent; the audited key was not used)
[2] OPENROUTER_API_KEY
surface=env gateway=openrouter reachable=true
detected=value_shape resolved=true
fingerprint: sk-or-v1-…(len=70) sha256:e7cceab978af41d5
- policy: network_mode "public": no egress control is configured, so the gateway endpoint is reachable and the credential is spendable
- http_no_auth: GET https://openrouter.ai/api/v1/models -> HTTP 200 (no auth header sent; the audited key was not used)
remediation: remove the real keys from the sandbox config, or run
benchgate gate benchgate.example.yaml -- <eval command>
to swap them for a local mock gateway before the eval starts.
$ echo $?
1exit 1 就是 CI 门禁:任何发现都会让这一步失败。清理后的配置:
$ benchgate audit examples/clean.yaml # findings: 0,exit 0,放行接进评测流水线(评测 job 之前):
- name: pre-bench credential gate
run: |
go install github.com/SuperMarioYL/benchgate/cmd/benchgate@v0.1.0
benchgate audit tasks/hello-world/task.toml
# exit 1 → 本步失败,评测 job 不会启动# 先干跑:改写配置 → 复验 → 还原,不启动评测
benchgate gate benchgate.example.yaml --dry-run
# 正式门禁:mock 注入后运行评测命令,结束后自动还原配置
benchgate gate benchgate.example.yaml -- harbor run --task tasks/hello-worldgate 会启动一个本地 OpenAI 兼容 mock 网关,把配置里所有真实凭据值换成占位 key、所有真实网关 URL 换成 mock 地址,复验"没有真实凭据形状、没有真实网关引用"后才把评测命令跑起来,并向评测进程注入 BENCHGATE_MOCK_URL。
约 30 秒的完整流程录屏(asciinema v2,由真实运行录制,脚本见 docs/demo/record.sh):
docs/demo/benchgate-audit.cast — audit 发现可达凭据并给出判定证据;gate 注入 mock 网关,复验零真实外联。
asciinema play docs/demo/benchgate-audit.cast录屏末段(gate 注入 mock 后,评测进程调用 chat/completions 只会命中本地 mock):
$ benchgate gate benchgate.example.yaml -- bash -c 'curl -s -X POST $BENCHGATE_MOCK_URL/chat/completions -d "{\"model\":\"deepseek-v4.1-flash\"}"'
gate: mock gateway at http://127.0.0.1:59196/api/v1
MODELSCOPE_SDK_TOKEN credential_value -> mock key (value_shape; ms-…(len=39) sha256:0b08e5ab4b6dcd18)
OPENAI_BASE_URL gateway_url -> http://127.0.0.1:59196/api/v1 (references gateway host openrouter.ai)
OPENROUTER_API_KEY credential_value -> mock key (value_shape; sk-or-v1-…(len=70) sha256:e7cceab978af41d5)
PROVIDER_BASE_URL gateway_url -> http://127.0.0.1:59196/api/v1 (references gateway host openrouter.ai)
gate: re-verified — no real credential shapes and no real gateway endpoints remain in the patched config
{"choices":[{"finish_reason":"stop","index":0,"message":{"content":"[benchgate-mock] This response came from the local mock gateway; no real provider was called.","role":"assistant"}}],"id":"chatcmpl-benchgate-mock","model":"deepseek-v4.1-flash","object":"chat.completion","usage":{"completion_tokens":0,"prompt_tokens":0,"total_tokens":0}}
gate: eval exited with code 0; mock gateway served 1 request(s) during the eval
单二进制 benchgate,无守护进程,四个内部阶段:
benchgate (Go 单二进制)
├── config loader──解析 task.toml / job config.yaml / compose,枚举 env、挂载与 network 策略
├── detectors ────凭据形状:OpenRouter sk-or-、ModelScope ms-、通用 sk-/Bearer、凭据命名与挂载文件
├── probe ────────network 策略可达性判定 + 无鉴权 GET 网关 /models(不带任何凭据)
├── mock gateway──本地 OpenAI 兼容 mock;gate 注入、复验、还原
└── report ───────脱敏账本(终端 / JSON / Markdown)+ CI exit code
账本里的每条发现只携带元数据:变量名、前缀类别、长度和 SHA-256 指纹(前 16 个十六进制字符)。凭据原文从不被打印或落盘——报告可以存档、diff,而密钥本身不会进任何日志。
| 能力 | v0.1 状态 |
|---|---|
Harbor task.toml(含 [agent] 相位覆盖、旧 allow_internet 字段)解析 |
已实现 |
Harbor job 级 config.yaml 与 docker-compose 解析(env 并集 + bind mounts) |
已实现 |
OpenRouter(sk-or-v1-/sk-or-)、ModelScope(ms- UUID)、通用 sk-/Bearer 值形状检测 |
已实现 |
凭据命名规则(*_KEY/*_TOKEN/*SECRET 等)与挂载文件(.env、id_rsa、*.pem 等)检测 |
已实现 |
| network 策略可达性判定(public / allowlist / no-network) | 已实现 |
网关 endpoint 无鉴权连通性探测(GET /models,不带凭据) |
已实现 |
脱敏账本:终端 / --json / --output report.md |
已实现 |
| CI exit code 门禁 | 已实现 |
| mock 网关(models / chat/completions / completions / key)+ 配置改写 + 复验 + 还原 | 已实现 |
docker exec 进入运行中的容器取证 |
未实现——审计是配置级的,见下 |
| 容器网络内自动可达的 mock 注入 | 未实现(路线图 v0.2),见下 |
| hosted 报告上传接口 | 未实现(见价格) |
两条如实说明,用前请读:
- 审计是配置级判定 + 无鉴权连通性检查,不是容器内取证。 可达性来自配置声明的 network 策略(
public判可达;allowlist按allowed_hosts匹配网关 host,匹配刻意宽松、宁可多报;no-network判不可达)。探测只做一次不带Authorization头的GET {gateway}/models,验证 endpoint 存活、记录状态码——被审计的 key 不会被使用,传输失败只记为"不确定",不改变判定。v0.1 不docker exec进容器。 - mock 默认绑定 loopback,对容器内模型不是自动可达。
gate在 v0.1 给出的是配置级保证:复验后的配置里没有真实 key、没有真实网关 URL,harness 用它启动的评测只会拿到 mock 地址。评测进程与 mock 同主机时(host 上直接跑的 harness、host 网络模式的容器)loopback 即可命中 mock;模型跑在独立 Docker 容器网络里时,还需要让 mock 对容器网络可达(--listen 0.0.0.0:端口并把 base URL 指向 host-gateway 地址),这一步 v0.1 不自动完成,v0.2 计划自动化。
这不是又一个密钥扫描器。各方看的是不同的东西:
| 工具 | 审计对象 | 时机 | 处置方式 |
|---|---|---|---|
| gitleaks / trufflehog | 仓库代码里已提交的密钥 | 提交 / CI 时 | 告警、阻止合并 |
| OpenRouter 花费告警 | 账单消耗 | 事后 | 止损:轮换 key |
| Harbor/Pier 网络隔离 | 容器出网 | 运行时 | 需显式配置,且评测必须保留网关可达 |
| BenchGate | 评测沙箱配置里模型可达的凭据 | 跑分前 | 换成 mock 网关 + exit code 门禁 |
BenchGate 审计的是环境而不是模型,缓解靠替换而不是检测;它不解决容器逃逸、prompt injection 或全量网络扫描,也不修改 Harbor/Pier 上游——刻意保持旁路,以中立覆盖 Harbor、Pier 与同构自研 harness。
| 输入 | 识别方式 | 提取内容 |
|---|---|---|
Harbor task.toml |
.toml 扩展名 |
[environment.env] 字符串对;network 策略取 [agent] 相位覆盖,缺省回落 [environment];旧字段 allow_internet = false 等价 no-network |
Harbor job 级 config.yaml |
.yaml/.yml 且无 services 键 |
environment.env 与 network_mode |
| docker-compose | .yaml/.yml 且有 services 键 |
各 service 的 env 并集(模型容器与 sidecar 同网络)、bind mounts;任一 service 可出网则整体按 public 处理 |
解析宽容:未建模的表和字段([task]、[metadata]、[verifier] 等)原样忽略,因此 Pier 与同构的自研 harness 配置也能解析。
| 策略 | 可达性判定 |
|---|---|
public(默认;未声明、bridge、host 等均归入此档) |
网关可达,凭据可消费 |
allowlist |
allowed_hosts 覆盖网关 host 才可达(精确项、子域与 *.后缀 通配;宽松匹配,宁可误报可达) |
no-network |
不可达,凭据不可消费 |
benchgate audit <task.toml|config.yaml|docker-compose.yaml>
--json 报告以 JSON 输出到 stdout
--output FILE 报告写入文件(.md 渲染 Markdown,其余 JSON),仍打印终端账本
--offline 跳过对网关 endpoint 的在线探测(离线审计主机)
benchgate gate <config> -- <评测命令>
--dry-run 执行改写、复验、还原,不启动评测
--listen ADDR mock 网关监听地址(默认 127.0.0.1:0)
exit code:0 无发现;1 有发现或运行出错。门禁判定刻意保持二元——单条发现的 reachable 细节放在报告里供人判断,exit code 不做策略化解释,避免门禁随政策漂移。
gate 的恢复是崩溃安全的:评测启动前原始配置另存 *.benchgate.bak,正常结束、SIGINT/SIGTERM 与评测自身退出码透传之外,即使 gate 进程被 SIGKILL,原始配置也在磁盘上。
- v0.1.x — 更多 provider 的凭据形状(通用
sk-/Bearer已覆盖主流 OpenAI 兼容网关);报告格式增强。 - v0.2 — 容器网络内自动可达的 mock 注入(host-gateway 自动化);
benchgate-action:CI 里一个 step 复用整套门禁。 - hosted —
benchgate upload报告上传接口、团队政策档与漂移告警(见价格)。 - 持续跟踪上游 — Harbor/Pier 若在默认值里剥离凭据或提供官方 mock(如 PR #3538 方向),
audit仍作为合规验证器保留;跟踪记录见 docs/evidence/repro.md。
Hosted 团队政策档(规划中;v0.1 不含任何 SaaS 组件,也不上传任何数据):$99/月/组织,前 10 家早鸟 $49/月。
面向要把"gate exit 0"写进组织评测政策的企业 CI 团队:
- 网关白名单与强制 gate 的组织政策档
- 评测配置漂移告警(每次 harness/模型 bump 自动重审)
- 报告留存 90 天
- 私有 mock 镜像
CLI 与自托管是全功能的 MIT 开源软件,hosted 层不锁任何本地能力。早期接入与需求登记:在 GitHub Issues 提 issue。
MIT © 2026 SuperMarioYL