全部技能 / 基础工具与工作流 / github-sensitive-data-cleanup
基础工具与工作流 · daymade/claude-code-skills

github-sensitive-data-cleanup

Scan and remove sensitive data (secrets, API keys, private domains/IPs, PII) from GitHub repository history. Use this skill whenever the user says "scan sensitive data", "clean git history", "remove secrets from repo", "sanitize GitHub history", "清理敏感数据", "历史重写", "force push", "泄露", or needs to repair a public repo after accidental secret/private context leakage. Also use before any force push to a public repository to verify visibility, backup, and scan results.

风险提醒:橙色 · 评估后使用AI 侦查报告
作者 daymadeGitHub daymade/claude-code-skills ↗Stars 1385许可 MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)commit d5c4678cb5
agent 宿主通常会约束 skill 执行权限;风险提醒为 AI 侦查观点,不构成质量或安全保证。第三方 skill 仅作拆解与展示,安装使用风险自负,版权归原作者。

1实现原理 · 为什么它能做到

整个 skill 是「流程编排(SKILL.md)+ 四个可执行 Python 脚本 + 三份参考文档」的组合;SKILL.md 只给步骤与纪律,机械动作全部委托给 scripts/ 下的脚本,脚本再用 subprocess 调 git / gitleaks / git-filter-repo / gh。

github-sensitive-data-cleanup/SKILL.md
The bundled scripts automate the mechanical parts:
注:这里在做什么:SKILL.md 本身没有一行 shell 逻辑,agent 读到的是「Step 0~7 + 7 条不可协商安全规则」,真正的执行体是四个独立 CLI 脚本。因此该 skill 的实际能力上限 = 这些脚本的能力上限,而非提示词措辞。

扫描是四层设计:Layer 1 gitleaks(密钥/token)、Layer 2 自定义正则(内网 IP、手机号、repo 专属 PII)、Layer 3 从用户私有 gitleaks 配置提取的私有域名/IP 规则 + 可选 identities 文件、Layer 4 由 agent 人工执行的 AI 语义审查。脚本自身只做 Layer 1~3,Layer 4 只是「打标记要求人工做」。

github-sensitive-data-cleanup/scripts/scan_repo.py
Layer 4: AI semantic review flag (must be performed manually by an agent)
注:四层在文件头 docstring 明写。关键设计:正则层永远抓不到「没人列出来过的」私有语境(真实姓名、项目代号、转录片段),所以脚本只能把这件事交回给模型/人,见 evidence 同文件 report 字典里的 ai_semantic_review_required: True。

「扫全历史」不靠 gitleaks 单独完成:自定义层与 Layer 3 层用 `git rev-list --all` 取全部 commit,再分批(batch_size=500)喂给 `git grep --perl-regexp`,逐批收集命中的 commit hash——目的是绕开命令行长度上限并避免只扫最近 N 个提交。

github-sensitive-data-cleanup/scripts/scan_repo.py
and to ensure the entire history is searched (not just the newest N).
注:实现细节:grep_all_commits() 内部对每个 pattern 调 git grep -n --perl-regexp -e <pattern> <batch...>;returncode==1 且无 stdout 视为本批无命中(不是错误);返回 set 去重后的 commit hash。verify_cleanup.py 直接 `from scan_repo import grep_all_commits` 复用同一实现,保证「扫」与「验」的行为不会漂移。

私有规则不进公开仓库:Layer 3 不复制任何私有域名清单,而是运行时从用户自己的 gitleaks TOML 里只抽两条规则 ID(private-domain-context / private-ip-context)的 regex,外加一个可选的逐行 identities 文件(每行 re.escape 后当字面量)。

github-sensitive-data-cleanup/scripts/scan_repo.py
LAYER3_RULE_IDS = ["private-domain-context", "private-ip-context"]
注:这里在做什么:解决了「公开 skill 不能内置真实私有域名」的悖论——规则留在用户侧(~/scripts/git-pii-guard/gitleaks.toml 或 .pii-patterns),skill 只提供提取器。DEFAULT_PATTERNS 里也刻意只放通用形态(10./172.16-31./192.168. 段与国内手机号),并在注释里写明 Do NOT hardcode real private domains here。TOML 解析优先用 stdlib tomllib(3.11+),旧解释器走一个只认三重引号/双引号 regex 的最小回退解析器。

重写前强制备份门禁:先 a) 确认 git-filter-repo 在 PATH 且可执行,b) 工作树必须干净(`git status --short` 有任何输出即 abort),c) `git bundle create --all` 备份并立刻 `git bundle verify`,任一步失败即退出且不进入重写。

github-sensitive-data-cleanup/scripts/rewrite_history.py
raise RuntimeError(f"Backup verification failed: {verify.stderr}")
注:主流程顺序:is_dir(.git) → replacements 文件存在 → message-replacements 文件存在 → shutil.which(git-filter-repo) → --version 可执行性探测 → check_clean_working_tree() → 打印 DESTRUCTIVE OPERATION 横幅 → 要求 --yes → 备份 → 重写。备份失败时捕获 CalledProcessError/RuntimeError 并 exit(1);重写中途失败时明确打印「Your backup is still available at: ...」。

重写本体是 `git-filter-repo --force --replace-text <file>`,可选追加 `--replace-message <file>` 覆盖提交信息通道——这是 v1.1.0 修掉的事故(只改 blob 会让实体名留在 commit message 里)。

github-sensitive-data-cleanup/scripts/rewrite_history.py
cmd += ["--replace-message", str(message_replacements_path)]
注:重写后写一份同名 .json 报告(backup_path.with_suffix(".json")),内含 old_heads/new_heads(用 `git show-ref --heads` 前后各采一次),并打印 NEXT STEPS:先 verify_cleanup.py 再 safe_push.py。注意 --force 是传给 git-filter-repo 的(解除其对 fresh-clone 的保护),不是 git push 的 force。

验证也是双通道,专门针对「两个检查共享同一个盲区」的失败模式:blob 通道对所有 commit 跑 git grep;message 通道跑 `git log --all --format=%H%x1f%B%x1e` 解析出每条提交信息再匹配,并且失败时输出命中的 commit hash(前 10 个)而非只给计数。

github-sensitive-data-cleanup/scripts/verify_cleanup.py
"--format=%H%x1f%B%x1e", "--no-color"],
注:待验证的 pattern 直接从重写时用的 replacements 文件反向提取(extract_patterns_from_replacements 解析 literal:/regex:/裸串),所以不会被重写后变化过的 .pii-patterns 干扰。任一条命中或任一检查出错,最终打印 VERIFICATION FAILED 并 exit(1);全清才打印 VERIFICATION PASSED。字面量 pattern 会先 re.escape 再交给 PCRE grep。

推送是 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。

github-sensitive-data-cleanup/scripts/safe_push.py
if "stale info" in result.stderr.lower():
注:这里在做什么:把「force push 前必须先证明这不是一个被别人 fork 走历史的公开库」变成机器可校验的步骤,而不是靠 agent 自觉。可见性判定的顺序是先校验、后确认(--yes)、最后推;缺 key 的报错文案是 Aborting rather than using fallback values.,即宁可停机也不猜。脚本从头到尾没有任何地方拼 --no-verify。

2核心能力

01四层敏感数据扫描(gitleaks + 自定义正则 + 私有上下文 + AI 语义审查标记)
02全历史(--all)而非仅 HEAD 的搜索,分批 git grep --perl-regexp,返回命中 commit 集合
03私有规则外置:只从用户私有 gitleaks 配置抽指定 rule id 的 regex,绝不把真实私有域名写进公开 skill
04带强制备份与备份校验的历史重写(git bundle create --all + bundle verify,失败即停)
05提交信息通道的重写与验证(--replace-message / git log --all 解析),修补 blob-only 盲区
06推送前仓库可见性与 fork 数核查,缺元数据即拒绝推送
07强制推送分级:--force-with-lease 优先,仅在 stale info 时一次性回退 --force;且拒绝 --no-verify
08事故知识库与人工升级条件:9 条真实事故 Lesson、私有规则外置策略、>50 forks 且含活密钥等场景必须停下来问人

3外部依赖

类型依赖
cligitleaks(Layer 1 密钥扫描)
cligitleaks detect(子进程实参:--source / --report-format json / --report-path / --verbose)
cligit-filter-repo(历史重写,须在 PATH 且 --version 可执行)
cligit(bundle create --all / bundle verify / status --short / show-ref --heads / rev-list --all / grep --perl-regexp / log --all / push --force-with-lease|--force)
cligh(GitHub CLI,用于仓库可见性与 fork 数核查)
apiGitHub REST API(由 gh repo view 隐式访问;源码内无字面 URL,端点归属 GitHub)
networkPyPI(文档命令 `uv run --with gitpython ...` 会要求 uv 从包索引获取 gitpython;四个脚本实际零 gitpython import,属文档声明依赖——[INFERENCE] 端点归属,源码内无 URL)
packagegitpython(SKILL.md 与四个脚本 docstring 的命令示例中声明;代码无 import,属未使用的声明依赖)
cliuv(文档规定的运行器:uv run --with gitpython / uv run scripts/...)
clidate(取 UTC 时间戳写入报告 scanned_at)
clibrew(仅安装说明:git-filter-repo / gitleaks / gh)
cliBFG 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 正是此类文档漂移导致的备份步骤被跳过)。
风险提醒:橙色,评估后使用。触发升档的是三条实际接触面:① 固有不可逆破坏能力——rewrite_history.py 直接对目标仓库跑 `git filter-repo --force`,safe_push.py 会向 GitHub 远端 force push(先 --force-with-lease,stderr 含 stale info 时一次性回退 --force),重写后 fork 与已缓存的旧历史无法回收(incident-lessons 的 195 forks 事故);② 读取用户私有清单——--identities-file / --gitleaks-config / .pii-patterns / replacements 文件里是真实私有域名、内网 IP、身份名与待脱敏明文;③ 远程包依赖——文档命令固定写 `uv run --with gitpython`,uv 需从包索引取包(且脚本实际并未 import gitpython)。同时该 skill 的门禁相当完整:干净工作树校验、bundle 备份 + verify、显式 --yes、gh 可见性校验(缺元数据即 abort)、lease-first、全脚本零 --no-verify,且 destructive 动作只在用户按 SKILL.md 主动跑脚本时发生——因此不落红色(无 TLS 降级、无反爬绕过、无任意代码执行/批量采集面)。只听扫描用法(scan_repo.py / verify_cleanup.py,默认不改仓库、无网络)时,实际接触面回落到蓝色;一旦执行重写或推送,按橙色对待,务必先自证 --repo/--branch/--backup 三个参数指向正确对象。

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结论

  • 重写历史这件事被拆成可校验的机器门禁,而不是靠 agent 自觉:干净工作树 → bundle 备份 → bundle verify → --yes → 重写,任一步失败即停。
  • 推送前置可见性核查是 fail-closed 的:gh 不可用或元数据缺 key 一律拒绝推送,绝不从 URL/目录名猜测公开性。
  • 双通道验证(blob + commit message)修掉了「两个检查共享同一盲区」的经典事故,且失败报告给出具体 commit hash 便于定位。
  • 公开仓库不内置私有知识:真实域名/IP/身份留在用户侧配置,skill 只提供提取器与匹配器,避免「清理工具本身成为泄露源」。
  • 自陈边界与不夸大:明确不做凭证轮换、不清理 GitHub 备份与 fork、不绕 hook,并要求「泄露即视为已被看见」。
  • 适合:适合:已经/怀疑把密钥、私有域名、内网 IP、PII 推上 GitHub,需要「先扫 → 备份 → 重写全历史(含提交信息)→ 双通道验证 → 可见性核查后推送」这一整套流程的个人或团队;也适合把「force push 前必须验证可见性与备份」固化成可执行纪律的仓库维护者。使用方式上,最稳的是先只跑 scan_repo.py/verify_cleanup.py(只读、无网络)确认命中,再由人复核 replacements 文件与 --repo/--remote/--branch/--backup 四个参数后执行重写与推送。
    不适合:不适合把它当作「泄露已被消除」的认证:它不改 GitHub 备份、不追回 fork、不代轮换凭证,也不保证语义层面的私有语境被找全(Layer 4 非确定性)。不适合无人值守/批量自动化地对多仓库批量重写与强推(脚本无远程/分支白名单,--yes 是唯一人工闸门);不适合期望不改写历史的最小干预场景(若只是新提交泄露,优先轮换凭证 + 单次 revert/gitignore 更划算);也不适合非 git 仓库或没有 force-push 权限、且无法接受既有 PR 分支失效的协作仓库。
    安装 agent 直装可复制
    ① 本站镜像 更新 2026-09-15
    方式 A · 人下载镜像包下载 github-sensitive-data-cleanup.tar.gz
    sha256: aa1ae4b1f420e74a…
    方式 B · JSON 格式安装指南,复制给 agent
    安装指南
    agent 读 JSON 指南后会自动从本站下载安装,无需更多说明。
    ② 上游 GitHub · 原始来源
    能访问 GitHub?直接去上游安装(实时版,可能已更新)GitHub 原始 ↗
    本页镜像锁定 commit d5c4678cb5;上游为实时仓库。
    来源信息 GitHub 原始
    作者 / 仓库daymade / daymade/claude-code-skills
    Stars1385
    最近推送2026-09-09
    本 skill commitd5c4678cb5
    许可MIT(仓库根 LICENSE 文件;GitHub API spdx MIT;Copyright (c) 2025 daymade)
    本站信息
    收录日期2026-09-06
    分类基础工具与工作流
    侦查报告AI 侦查 · 2 遍 · 2026-09-06
    本站镜像与 GitHub 原始是不同来源:本站锁定 commit 快照经 /r2 分发;GitHub 为实时上游,内容可能已更新。
    同分类邻近