已知的假密钥,标记一次就够了
只要你让 agent 写过 AWS 示例代码,会话记录里多半就有 AKIAIOSFODNN7EXAMPLE。这是 AWS 官方文档里的示例密钥,完全符合 AWS 的格式,于是 spillscan 每次都会报它。测试里的 fixture token、从教程里粘过来的 JWT 也一样。同一条无害的结果看到第三遍,人就开始草草扫过报告。总是误报的警报,最后只会让人学会无视它。
所以 spillscan 现在会读取一份白名单。关键在于名单里写什么。
写指纹,不写值
每条结果现在多了一列 FP:对值做 SHA-256(混入 spillscan 专用前缀)后取前 12 位十六进制。它能认出这个值,但不包含这个值。
$ spillscan scan RULE PREVIEW FP AGENT FILE:LINE aws-akia AKIA…MPLE 8cc03843ecd0 claude-code ~/.claude/projects/…/x.jsonl:128 github-pat ghp_…a91F 1d6e40b2c9f7 claude-code ~/.claude/projects/…/x.jsonl:128 ... Known-fake value? Add the line fp:<FP> to ~/.config/spillscan/allow.txt; scan and redact then skip it.
把认得出的那一个抄进 ~/.config/spillscan/allow.txt:
fp:8cc03843ecd0 # AWS 文档示例密钥
为什么不让人直接写值?因为白名单恰恰是会被提交进 dotfiles、发给同事、贴进 issue 的那个文件。如果里面存的是值,哪天有人把一个以为是假的密钥加进去,白名单本身就成了新的泄露点。存指纹的话,最坏也只是一段 12 位的哈希。另有 value: 用来写那些本来就是假的字面值,文档里也写明了它会原样保存你输入的内容。
也可以按规则和位置
有时候噪音是一整类:比如你做认证教程那几个会话里的所有 JWT。同一行的多个条件必须同时满足,不同行之间是“或”。
rule:jwt path:~/.claude/projects/*auth-tutorial*/** path:~/.codex/sessions/2026/01/** rule:generic-assign
glob 匹配的是会话文件的绝对路径:* 不跨目录,** 可以跨,~/ 是家目录,不含斜杠的 glob 只匹配文件名。写错了,比如 rule:jtw,会直接报错退出:allow.txt:1: unknown rule "jtw" (did you mean rule:jwt?)。一份悄悄什么都匹配不上的白名单,比没有更糟。
藏起来的东西,都留了记录
隐藏结果是这个功能里有风险的那一半,所以 spillscan 会明说:表头写着 allowlisted=1,末尾一行注明是哪份名单藏的,spillscan scan --no-allow 能看到原始读数。被白名单命中的结果不会导致退出码 2,CI 检查可以通过,但 --json 里仍然有这个计数。redact 对这些值原样保留,并报告保留了几个。MCP 的 scan_report 遵循默认白名单,而且依然只读。
额外的名单用 --allow FILE 传入,可以重复。scan 给出的后续命令会带上它们,所以你照抄的那条清洗命令,跳过的正好是这次扫描隐藏的那些。
测试抓到的一个 bug
PEM 私钥跨多行。在 JSONL 文件里,这些换行存成 \n 转义;JSON 解码之后则是真正的换行。scan 对原始字节算指纹,redact 遍历的是解码后的 JSON,于是同一把密钥得到两个不同的指纹,加进白名单的 PEM 块照样会被清洗。现在 redact 两种形式都会检查,而且有一个测试专门确保去掉这个检查就会失败。
该功能已在 main 上,会随下一个版本发布。格式细节见 Configuration;源码与 README。最后提醒一句:只有确认是假的才该加白名单。真泄露的密钥,照样要去轮换。
— 2026-10-28