v0.3.1 增量:历史 Secret 检查与脱敏¶
历史增量说明。 本文记录 v0.3.1 开发阶段的 secret-history 增量;v0.5.0 当前完整边界以安全文档和发布说明为准。
基线:main / 8739b01318b27ff253d45ce7d3e76350622af83a。
这是独立候选补丁,不代表 v0.3.1 全部完成或已发布。
修复¶
旧 finish 仅扫描 base 到当前工作树的净 diff。如果提交 A 引入凭证、提交 B 删除凭证,最终 diff 可以没有该内容,但 A 仍可能被推送。
现在 finish 保留原有候选 diff 检查,额外扫描固定 base_sha..HEAD 范围内
各提交的新增文本,包括合并提交相对于每个 parent 的独立 diff。
不根据历史 pushed 标记或 cached remote-tracking ref 跳过扫描;已推送但
仍位于这个范围内的提交会再次检查。这是有意的保守策略,不是精确的 Git
网络传输对象集合,也不是对仓库全部历史的审计。
同时修复:
EXAMPLE、YOUR_TOKEN等词出现在一行中,不再豁免整行的凭证匹配。 自动 whole-line placeholder 例外已移除。模板使用<token>等不满足 真实凭证格式的值;credential-shaped 测试值需要显式误报审阅。- 用 hunk 边界识别新增行,避免把以
++开头的文件内容误当作 diff header。 - 按 LF 而不是所有 Unicode 行分隔符拆分 patch,避免漏掉同一新增行后半段。
- finding 不返回原始命中源码行,而返回类型、路径及可用的 commit SHA。
- finish 的展示 diff 使用脱敏版本;snapshot 仍绑定原始 diff,不能因两个值 脱敏后相同而复用旧 review。
- 脱敏覆盖引号中的普通 secret assignment,以及完整/被截断的 private-key block。
失败关闭与边界¶
历史扫描最多 256 个提交、8 MiB patch,最长 300 秒,且不能超过用户命令 超时配置。Git 输出使用有界读取,不先无限量 capture 再截断。
浅克隆、不可读对象、非祖先 base、legacy grafts、超限/超时、非 UTF-8/NUL
二进制历史、submodule 变化等无法获得该文本扫描范围的完整证据时,finish
被阻断,并报告 history coverage_complete: false。--allow-secret-match
只能覆盖完整扫描中的显式误报,不能把缺失的扫描证据当作成功。
固定的本地 Git 读取不调用 diff/textconv helper,不使用 replace objects, 不交互获取凭证,也不允许扫描过程进行 Git 协议访问。
这仍是高信号格式的启发式检测,不是 DLP 或 sandbox。扫描范围不包含 base
之前的历史、commit message、外部 LFS 对象、submodule 内部内容或任意编码
后的秘密。原有候选 working-tree diff / reviewability 边界并未被替换为
完整 blob/index 审计;候选路径解析、Git filter/attribute 等更广泛边界需要
后续独立加固。coverage_complete 仅描述已声明扫描范围是否完整执行,不是
“没有任何形式秘密”的保证。
本补丁不改变低层手工 commit/push 路径,不引入 history rewrite、force-push、 自动 merge、额外模型调用或权限扩展。MCP job-log 与 CLI CI-log 的统一有界 抓取/脱敏、进程树控制、handoff 统一和 workspace locking 仍在后续清单中。
遇到真实凭证¶
先撤销/轮换,再人工处理尚未发布历史。仅从工作树删除文件并新建提交不足以
消除历史中的凭证。不要用 --allow-secret-match 放行真实凭证。
工具不会替用户自动改写已发布历史。
验证¶
test_history_scan.py 包含真实临时 Git 历史测试与 scanner 回归测试;
test_finish_history.py 使用真实 WorkspaceManager 验证历史/candidate 阻断、
零写入、误报覆盖边界及 snapshot 绑定;原 finish 单元测试保留并在 history
I/O 边界使用 mock,真实集成测试不 mock scanner。
uv run python -m unittest discover -s tests -v
Git 语义参考:官方 git-log(--diff-merges=separate、--text、
--no-textconv)和 git-replace(--no-replace-objects)文档。