S11 · harden-task-runner-credentials 验证 token 不写盘
目标:验证改用 git credential helper 后,install token 物理上不写入 .git/config(无论 clone / 续期 / fetch / push 哪条路径),跟 POC E1/E2 实测的 https://x-access-token:$TOKEN@github.com/... URL 注入方式形成对比。
日期:2026-05-19
脚本:/tmp/spike-token-leak.mjs
结果:✅ 5/5 assertions 通过。
设计
┌───────────────────────────────────────────────────────────────┐
│ spike-token-leak.mjs │
│ │
│ 1. 起本地 bare repo (file:// 协议) 作 clone target │
│ 2. 模拟 run.js clone 流程: │
│ git -c credential.helper=<inline-sh> clone file://upstream│
│ env: INSTALLATION_TOKEN=ghs_fake_secret_NEVER_on_disk │
│ 3. clone 后 git config credential.helper <inline-sh> │
│ 4. cat .git/config · 断言无 token 字面值 │
│ 5. 改 env (新 token) + fetch · 断言 .git/config 仍无 token │
│ │
└───────────────────────────────────────────────────────────────┘5/5 assertions
| # | 断言 | 结果 |
|---|---|---|
| 1 | .git/config 不含 ghs_ 前缀的 token 字面值 | ✓ |
| 2 | .git/config 不含 spike 注入的 TOKEN_LITERAL 字面值 | ✓ |
| 3 | .git/config 含 credential.helper 配置(引用 $INSTALLATION_TOKEN env) | ✓ |
| 4 | .git/config 的 remote.origin.url 不含 x-access-token:<TOKEN>@ 注入格式 | ✓ |
| 5 | token 续期路径(改 env 后再 fetch)· .git/config 仍无 token 字面值 | ✓ |
实测 .git/config 内容
[core]
...
[remote "origin"]
url = file:///var/folders/.../upstream.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
[credential]
helper = "!sh -c 'echo \"username=x-access-token\"; echo \"password=$INSTALLATION_TOKEN\"'"关键点:
[remote "origin"].url仅含 file:// 路径,无 token 前缀(对比 POC E1/E2 时该行是https://x-access-token:ghs_xxx@github.com/...)[credential].helper字符串内引用的是变量名$INSTALLATION_TOKEN,不是变量值——cat .git/config永远拿不到 token 字面值
对比 POC 实测(PR #12 时)
| 路径 | POC(before) | 本 change(after) |
|---|---|---|
.git/config 含 token 字面值 | ✗ 是(明文) | ✓ 否 |
续期需要 git remote set-url | ✗ 是 | ✓ 否 — 仅 process.env.INSTALLATION_TOKEN = newToken |
| 容器 destroy 后磁盘镜像泄漏风险 | ✗ 高 | ✓ 无 |
printenv 仍可读 token | 是 | 是(接受 · 设计 Non-Goal) |
| Token blast radius | 整 install 范围 | 单 repo(由 Plan F 兜底) |
仍存在的洞(Non-Goals · 设计明确接受)
- agent 调
Bash("printenv")仍能读$INSTALLATION_TOKEN字面值 - agent 调
Bash("cat /proc/self/environ")仍能读 - 这两路径都需要攻击者明确指挥 agent(不像
cat .git/config是 agent 自然行为) - 由 Plan F(install token scoped 到单 repo + 2 项 permissions)兜底单 token 滥用损害
- 由 Plan E 文档化的「网络出口未限制」承认 partial 防护
已知风险 / Limitation
- 本 spike 在 macOS 本地跑 · 用 file:// 协议绕过 HTTPS 鉴权
- 实际 Fly Machine 容器内跑时是 HTTPS clone github.com · credential helper 路径走 stdin 喂凭证
- 后续真跑(任务 §7.3)需要在 Fly 上验证一遍 PR 创建成功 + scoped token + 容器内
.git/config不含 token