1实现原理 · 为什么它能做到
整个 skill 是「流程编排(SKILL.md)+ 四个可执行 Python 脚本 + 三份参考文档」的组合;SKILL.md 只给步骤与纪律,机械动作全部委托给 scripts/ 下的脚本,脚本再用 subprocess 调 git / gitleaks / git-filter-repo / gh。
The bundled scripts automate the mechanical parts:
扫描是四层设计:Layer 1 gitleaks(密钥/token)、Layer 2 自定义正则(内网 IP、手机号、repo 专属 PII)、Layer 3 从用户私有 gitleaks 配置提取的私有域名/IP 规则 + 可选 identities 文件、Layer 4 由 agent 人工执行的 AI 语义审查。脚本自身只做 Layer 1~3,Layer 4 只是「打标记要求人工做」。
Layer 4: AI semantic review flag (must be performed manually by an agent)
「扫全历史」不靠 gitleaks 单独完成:自定义层与 Layer 3 层用 `git rev-list --all` 取全部 commit,再分批(batch_size=500)喂给 `git grep --perl-regexp`,逐批收集命中的 commit hash——目的是绕开命令行长度上限并避免只扫最近 N 个提交。
and to ensure the entire history is searched (not just the newest N).
私有规则不进公开仓库:Layer 3 不复制任何私有域名清单,而是运行时从用户自己的 gitleaks TOML 里只抽两条规则 ID(private-domain-context / private-ip-context)的 regex,外加一个可选的逐行 identities 文件(每行 re.escape 后当字面量)。
LAYER3_RULE_IDS = ["private-domain-context", "private-ip-context"]
重写前强制备份门禁:先 a) 确认 git-filter-repo 在 PATH 且可执行,b) 工作树必须干净(`git status --short` 有任何输出即 abort),c) `git bundle create --all` 备份并立刻 `git bundle verify`,任一步失败即退出且不进入重写。
raise RuntimeError(f"Backup verification failed: {verify.stderr}")
重写本体是 `git-filter-repo --force --replace-text <file>`,可选追加 `--replace-message <file>` 覆盖提交信息通道——这是 v1.1.0 修掉的事故(只改 blob 会让实体名留在 commit message 里)。
cmd += ["--replace-message", str(message_replacements_path)]
验证也是双通道,专门针对「两个检查共享同一个盲区」的失败模式:blob 通道对所有 commit 跑 git grep;message 通道跑 `git log --all --format=%H%x1f%B%x1e` 解析出每条提交信息再匹配,并且失败时输出命中的 commit hash(前 10 个)而非只给计数。
"--format=%H%x1f%B%x1e", "--no-color"],
推送是 fail-closed 门禁:先 `gh repo view --json visibility,isPrivate,stargazerCount,forkCount,owner,name`(用 cwd 推断仓库,不猜 owner/repo 字符串);gh 失败或返回缺 key 一律 abort,不做任何 fallback 猜测;public 且 forkCount>0 时打印 WARNING;没有 --yes 直接退出;推送先 --force-with-lease,只有 stderr 出现 stale info 才允许回退一次 --force。
if "stale info" in result.stderr.lower():
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| cli | gitleaks(Layer 1 密钥扫描) |
| cli | gitleaks detect(子进程实参:--source / --report-format json / --report-path / --verbose) |
| cli | git-filter-repo(历史重写,须在 PATH 且 --version 可执行) |
| cli | git(bundle create --all / bundle verify / status --short / show-ref --heads / rev-list --all / grep --perl-regexp / log --all / push --force-with-lease|--force) |
| cli | gh(GitHub CLI,用于仓库可见性与 fork 数核查) |
| api | GitHub REST API(由 gh repo view 隐式访问;源码内无字面 URL,端点归属 GitHub) |
| network | PyPI(文档命令 `uv run --with gitpython ...` 会要求 uv 从包索引获取 gitpython;四个脚本实际零 gitpython import,属文档声明依赖——[INFERENCE] 端点归属,源码内无 URL) |
| package | gitpython(SKILL.md 与四个脚本 docstring 的命令示例中声明;代码无 import,属未使用的声明依赖) |
| cli | uv(文档规定的运行器:uv run --with gitpython / uv run scripts/...) |
| cli | date(取 UTC 时间戳写入报告 scanned_at) |
| cli | brew(仅安装说明:git-filter-repo / gitleaks / gh) |
| cli | BFG Repo-Cleaner(参考文档里的备选工具,需 Java;未被脚本调用) |
| network | 用户侧的私有规则/身份清单文件(非网络端点,但属外部运行时输入):私有 gitleaks.toml、identities.txt、repo 根 .pii-patterns |
4风险提醒 风险提醒:橙色 · 评估后使用
- 固有不可逆破坏面:脚本会对 --repo 指定的仓库原地跑 git-filter-repo --force,并对 --remote/--branch force push。 — 参数给错(指向另一个仓库、错分支)即造成历史改写;脚本只校验 .git 存在、工作树干净与 --yes,不校验默认分支、不校验 remote 目标、不在 force 回退前复跑验证。执行前应由人确认三个路径参数,并对高风险仓库留副本。
- fork 与 GitHub 侧缓存使清理不彻底,且 safe_push.py 对此只有警告。 — 判定 `if not info["isPrivate"] and info["forkCount"] > 0:` 仅打印 `WARNING: This is a PUBLIC repository with {forkCount} forks.`,不阻断;incident 记录过的 195 forks 场景下旧历史仍留存在每个 fork 与其缓存视图中,补救需轮换凭证并联系 GitHub Support/通知 fork owner(人工动作)。
- 验证器的能力边界可能被误读为「已验证干净」。 — Layers 1-3 都是模式匹配(且对非 UTF-8 内容边界明确不覆盖),Layer 4 由模型进行、非确定性且可能幻觉;SKILL.md/脚本把 ai_semantic_review_required 固定为 true 并把 VERIFICATION PASSED 措辞限定为「no known sensitive patterns remain in history」,但操作者仍可能把 PASSED 当作彻底安全的认证。
- 敏感明文在流程中多处落地:replacements 文件、backup bundle 及同名 .json、--output 扫描报告、identities/私有 gitleaks 配置,以及 Layer 4 把历史片段粘进模型提示词。 — 文档已要求 replacements 放仓库外并不得提交 .pii-patterns,但 /tmp 中的副本、bundle 与报告仍含真实私有域名/身份与旧历史;执行后应清理这些中间产物,并在使用远端模型做 Layer 4 前确认该外发可接受。
- 依赖与文档漂移:文档命令固定 `uv run --with gitpython`(脚本并未使用该包),且同一文档内 Step 4/5/6 与 Step 1/1.5 的调用形式不一致。 — 多余包拉取属可避免的供应链接触面与网络访问;文案不一致也容易让执行者照抄出无 --yes 的变体(incident-lessons Lesson 9 正是此类文档漂移导致的备份步骤被跳过)。
5第二遍独立确认
- [ok] 文件清单全量核对(有无未列出的隐藏脚本/数据文件) — glob(hidden=true, gitignore=false)结果仅 9 个文件:SKILL.md、evals/evals.json、references/{ai_semantic_review_prompt,incident-lessons,tooling_notes}.md、scripts/{rewrite_history,safe_push,scan_repo,verify_cleanup}.py。无隐藏文件、无二进制、无 .pii-patterns 样本、无内置 hook;仓库级的 PII Guard hook(~/scripts/git-pii-guard)与 .githooks/ 属仓库/机器环境,不在 skill 目录内,skill 只是要求不得绕过它。
- [ok] external_deps 逐条反查调用点 — 全部命中:gitleaks → scan_repo.py `gitleaks_bin = shutil.which("gitleaks")` + verify_cleanup.py 同名调用;git-filter-repo → rewrite_history.py `shutil.which("git-filter-repo")` 且随后 `--version` 探测;git → show-ref/status/bundle create/bundle verify/rev-list/grep/log/push 各自 subprocess.run 均可定位;gh → safe_push.py 的 `["gh", "repo", "view", "--json", ...]`;date → scan_repo.py `["date", "-u", "+%Y-%m-%dT%H:%M:%SZ"]`;uv/brew 只出现在文档命令与 Prerequisites,无对应代码调用(已在条目 note 中说明)。
- [ok] 安全结论反例搜索:是否存在漏掉的网络调用、隐藏外发或混淆内容 — 未找到反例,安全结论成立(记录检索范围以免过度外推):对 skill 目录 grep `requests|urllib|socket|curl|wget|http://|https://` 零命中,且全目录无任何 http(s):// 字面 URL(连「联系 GitHub Support」一段也只用文字);无 base64/fromhex/codecs/eval/exec/__import__ 等混淆或动态执行构造,无 60 字符以上的不透明长 token;对 git / git-filter-repo / gitleaks / gh / date 之外的二进制调用为零。唯一出网路径是 safe_push.py 经 gh 访问 api.github.com(uv --with gitpython 的包索引访问属 uv 行为而非脚本行为,已在 external_deps 标注 [INFERENCE] 端点)。证明边界:这是对该 commit 下 9 个文件的静态核对,不含运行时行为。
- [ok] 实现原理是否夸大:SKILL.md「What the Bundled Scripts Do」逐条对代码 — 四条声明全部落地:scan_repo.py 确实同跑 gitleaks 与自定义 grep 层并输出 JSON;rewrite_history.py 确实先备份再 `git filter-repo --replace-text`,且 `--message-replacements` 确实映射到 `--replace-message`;verify_cleanup.py 确实同时查 blob(git grep 全 commit)与 commit message(`git log --all --format=%H%x1f%B%x1e`),使漏改 message 的重写必然失败;safe_push.py 确实调 gh repo view 并走 lease-first。SKILL.md 未出现代码做不到的承诺。
- [ok] 7 条不可协商安全规则在 SKILL.md 的位置与代码落地情况 — SKILL.md 第 64~77 行「Safety Rules (Non-Negotiable)」编号 1~7:scan-before-decide / backup-before-rewrite / gh repo view 验可见性且不得从 URL 或目录名推断 / 绝不 --no-verify / --force-with-lease 优先且仅在 remote ref stale 时回退 --force / 重写后必须重新扫描 + AI 语义审查 / 公开且带 forks 需格外小心。落地核对:规则 2、4、5 有代码门禁(备份+verify、脚本从不加 --no-verify、lease→stale info→force 一次);规则 3 有硬门禁(gh 失败或元数据缺 key 即 abort);规则 1、6、7 依赖 agent 遵守文档与人工动作(scan 结果、--yes、fork 通知),不由代码强制——这是规则与实现的唯一不对称处。
- [ok] safe_push.py 的 public+forks 判定与 force-with-lease 回退条件 — 判定式为 `if not info["isPrivate"] and info["forkCount"] > 0:`,命中时只向 stderr 打印 `WARNING: This is a PUBLIC repository with {forkCount} forks.`,不阻断流程;唯一的硬门禁是缺 --yes 时 exit(1)。回退条件为 `if "stale info" in result.stderr.lower():`(对应 git 对 force-with-lease 拒绝时的 (stale info) 文案),回退仅一次 (--force),再失败即 exit(1)。注意:脚本不校验 remote/branch 是否与默认分支一致,也不在回退前复跑验证——--repo/--remote/--branch 参数的正确性由操作者负责。
- [discrepancy] 文档声明的依赖 gitpython 是否真实被使用 — 四处 docstring 与 SKILL.md 多处命令写 `uv run --with gitpython scripts/...`,但四个脚本的 import 块逐行核对后只有 argparse/json/re/shutil/subprocess/sys/tempfile/tomllib/pathlib,零 `import gitpython` 或 `from git import`。即文档要求 uv 从包索引拉一个不使用的第三方包(多余供应链接触面),去掉 `--with gitpython` 不影响任何功能;此外同一份 SKILL.md 内部不一致:Step 4/5/6 的命令写 `uv run scripts/...`(不带 --with),Step 1/1.5 与「What the Bundled Scripts Do」写 `uv run --with gitpython`。
- [discrepancy] --gitleaks-config 是否被传给 gitleaks 本体 — run_gitleaks 的 cmd 只有 `detect --source --report-format --report-path --verbose`,没有 `--config`;用户私有 gitleaks.toml 仅被 parse_gitleaks_rules 解析出 private-domain-context / private-ip-context 两条 regex 供 Layer 3 的 git grep 使用。SKILL.md 的说法(该 flag 用于从私有配置读取这两条规则)与代码一致,不存在夸大;但若用户以为私有配置会整体驱动 gitleaks 扫描(例如自定义 allowlist/其它私有规则),实际不会——gitleaks 走自身默认配置与仓库本地 .gitleaks.toml。此项为「文档未声明但易误读」的行为差异,非虚假声明。
6结论
aa1ae4b1f420e74a…d5c4678cb5