github-sensitive-data-cleanup について
GitHub Sensitive Data Cleanupは、Gitリポジトリをスキャンし、履歴から機密情報を除去して、書き換えた履歴をGitHubへ安全に送るスキルです。スキャン、バックアップ、書き換え、検証をスクリプトで行い、安全確認に失敗した場合は人の確認を求めます。
主な機能
- 01シークレットと非公開情報のスキャン
- 02履歴書き換え前のバックアップ
- 03git-filter-repoによる履歴書き換え
- 04書き換え後のクリーンアップ検証
- 05プッシュ前のリポジトリ公開範囲確認
- 06安全確認失敗時の人による確認
活用シーン
- 01リポジトリ内の漏えいしたシークレットや非公開情報の発見
- 02Git履歴からの機密情報の除去
- 03クリーンアップ後の制御された強制プッシュ
- 04指定された露出箇所と残存リスクの評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ daymade/ claude-code-skills --skill github-sensitive-data-cleanupインストール後、Claude Code で/github-sensitive-data-cleanupを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/ai_semantic_review_prompt.mdincident-lessons.mdtooling_notes.mdscripts/rewrite_history.pysafe_push.pyscan_repo.pyverify_cleanup.py詳細
GitHub の機密データのクリーンアップ
概要
このスキルでは、Git リポジトリの履歴から機密データを安全に削除し、クリーンアップした履歴を GitHub にプッシュする手順を説明します。実際のインシデントから得られた貴重な教訓を反映しています。まずスキャンし、書き換える前にバックアップを作成し、書き換えた後に検証し、公開リポジトリの公開範囲とフォーク数を確認せずに強制プッシュしないでください。
同梱スクリプトは、機械的な作業を自動化します。
scripts/scan_repo.py— リポジトリをスキャンして、シークレットや非公開のコンテキストを検出します。scripts/rewrite_history.py— バックアップを作成し、git-filter-repoを使って履歴を書き換えます。scripts/verify_cleanup.py— 機密情報が削除されたことを確認します。scripts/safe_push.py— リポジトリの公開範囲を確認し、安全にプッシュします。
このスキルは、意図的に保守的な設計になっています。 安全チェックに失敗すると、処理を続けずに停止し、人間による確認を求めます。
このスキルを使用する場面
ユーザーが次のような場合に、このスキルを実行します。
- 「機密データをスキャンして」「リポジトリに漏えいがないか確認して」などと言った場合。
- 「Git の履歴をクリーンアップしたい」「履歴を無害化したい」「履歴を書き換えたい」「履歴からシークレットを削除したい」と希望した場合。
- 公開リポジトリにシークレット、非公開ドメイン、内部 IP アドレス、個人識別情報(PII)を誤ってプッシュした場合。
- (機密データの有無にかかわらず)公開リポジトリに強制プッシュしようとしている場合。
git filter-repo、BFG、git-filter-branch、または履歴の書き換えについて言及した場合。
前提条件
これらのツールを、マシンごとに一度インストールしてください。
# git-filter-repo (modern replacement for git-filter-branch)
brew install git-filter-repo
# gitleaks (secret scanner)
brew install gitleaks
# GitHub CLI
brew install gh
スクリプトは、git-filter-repo と gitleaks が PATH に設定されていることを前提とします。このスキルは、破壊的な操作を実行する前にこの点を確認します。
安全ルール(必須)
- 判断する前にスキャンしてください。 推測に基づいて履歴を書き換えてはいけません。
- 書き換える前にバックアップを作成してください。
git bundleまたは新しいベアクローンを使用してください。 - プッシュの前に
gh repo viewでリポジトリの公開範囲を確認してください。 URL やディレクトリ名から公開/非公開を推測してはいけません。 - フックを回避するために
--no-verifyを使用してはいけません。 PII Guard フックが失敗した場合は、根本的な問題を修正するか、許可リストを追加してください。回避してはいけません。 - 保存済みのリモートコミットに明示的に紐付けたリースを1つ使用してください。 リースが拒否された場合はプッシュを停止してください。同時に行われた作業を回避するために
--forceに切り替えたり、想定コミットを更新したりしてはいけません。 - 書き換えた後に検証してください。
git logがクリーンなだけでは不十分です。スキャナーを再実行し、AI による意味的なレビューも行ってください。 - フォークがある公開リポジトリでは、特に注意してください。 フォークやクローンに古い履歴が残っている可能性があります。対応方法を選ぶ前に、名前が挙がっている情報漏えいと所有者を確認してください。所有者への連絡は、許可された宛先とメッセージの範囲内でのみ行ってください。
ワークフロー
ステップ 0:リポジトリのパスと現在のブランチを確認する
cd /path/to/repo
git status --short
git remote -v
リポジトリの正確なルートを指定してください。スキャンと検証では、通常のリポジトリ、リンクされたワークツリー、ベアリポジトリのルートを使用できます。書き換えには独立したクローンが必要です。リンクされたルート、およびワークツリーが関連付けられた通常のリポジトリやベアリポジトリは拒否されます。ベアミラーには作業ツリーの状態がないため、代わりに Git の識別情報を確認してください。
ステップ 0.5:公開されている各箇所を特定する
対応するエージェントは、既知の検出内容が存在する正確な公開箇所と、許可された修正範囲を非公開のタスク証拠に記録します。際限のないリポジトリ全体の棚卸しではなく、検出内容に従ってください。以下の箇所は区別してください。
| 箇所 | 判断の根拠と修正の担当 |
|---|---|
| 現在のファイルとホスト上で公開されているテキスト | 指定された公開ファイル、PR のタイトル/本文、または影響を受けるその他のテキストを確認します。通常の修正で解消されるのは、現在の値のみです。ホスト上の編集には github-ops を使用してください。 |
| Git オブジェクトと参照 | ファイルの blob、コミットメッセージ、影響を受けるブランチ/タグの履歴を不変のオブジェクトに関連付けます。このスキルが担当するのは、明示的に許可された書き換えと、正確な参照先の読み戻しです。先端がクリーンでも、履歴がクリーンであることや古いオブジェクトが削除されたことの証明にはなりません。 |
| PR 本文の編集履歴 | 現在の本文が修正されている場合も、指定された PR の改訂履歴を確認します。github-ops の Pull Request Operations リファレンスにある PR 本文の編集履歴のクリーンアップ を使用してください。正確な改訂版の選定、非公開バックアップ、削除の許可、保持状態の読み戻しを担当します。 |
| キャッシュされた表示、PR の Git 参照、フォーク、クローン | 既知の参照先と所有者をそれぞれ確認します。Git の書き換えやブランチの削除だけでは、これらからの消去が証明されるわけではありません。サードパーティーのコピーや GitHub が管理する参照には、別の手段が必要になる場合があります。 |
非公開バックアップに保持されているオブジェクトと、引き続き公開取得可能なオブジェクトを区別してください。匿名アクセスについて主張する場合は、認証なしで正確な参照先をテストし、返された内容を意図した不変オブジェクトと比較してください。認証済みの読み取り、エラーページ、リダイレクトだけでは、匿名での公開や削除の証明にはなりません。検出内容が示している場合に限り、リリース、添付ファイル、ログを含めてください。確認していない箇所は未検証のままにしてください。
指定された各箇所について、何を確認したか、その結果から何が証明されるか、何が引き続き公開または未検証かを報告してください。同梱の Git スキャナーは、ホスト上の PR 改訂履歴を確認せず、キャッシュやフォークからの削除も証明しません。完了した範囲内で検出がなければ、その監査を終了してください。それによって、対象外または不明な箇所までクリーンになるわけではありません。承認済みの変更を検証したら、書き換え、削除、外部への連絡へと暗黙のうちに範囲を広げず、残存事項を報告してください。
ステップ 1:機密データをスキャンする
スキャナーを実行して、削除が必要なものを見つけます。
uv run --with gitpython scripts/scan_repo.py --repo /path/to/repo --output /tmp/scan-report.json
スキャナーは、リポジトリのルートにある .pii-patterns から、リポジトリ固有のパターンを自動的に読み込みます。このファイルに実際の非公開ドメインが含まれている場合は、コミットしないでください。.gitignore に追加するか、リポジトリの外に置いてください。作業ツリーに未追跡ファイルがある場合、rewrite_history.py は中止します。
レイヤー 3(gitleaks の設定と任意の ID ファイルからの非公開インフラストラクチャのコンテキスト)を有効にするには、次を実行します。
uv run --with gitpython scripts/scan_repo.py \
--repo /path/to/repo \
--gitleaks-config ~/scripts/git-pii-guard/gitleaks.toml \
--identities-file ~/.config/github-sensitive-data-cleanup/identities.txt \
--output /tmp/scan-report.json
--gitleaks-config フラグは、非公開の gitleaks 設定から private-domain-context と private-ip-context のルールを読み込みます。実際のパターンは非公開の設定に保持されます。公開されているこのスキルにコピーされることはありません。
/tmp/scan-report.json を確認してください。次の内容が含まれます。
gitleaksの検出内容(シークレット、API キー、トークン)。- カスタムパターンの一致(内部 IP、電話番号、個人情報)。
- レイヤー 3 のコンテキスト一致(設定にある非公開ドメイン、IP、ID)。
- 正規表現では検出できない内容について、AI による意味的レビューを行うよう促すリマインダー。
検出内容を解釈する前に、スキャンが正常に完了したことを確認してください。実行エラーが発生した場合、スキャンは未完了です。検出がゼロでもステップ 1.5 に進みます。クリーンという結果を承認するものでも、履歴の書き換えを許可するものでもありません。
ステップ 1.5:AI によるセマンティックレビュー(レイヤー 4)
正規表現スキャナー(レイヤー 1~3)では、実名、プロジェクトのコードネーム、トランスクリプトの抜粋、社内会議への言及、アーキテクチャの説明など、新たな非公開コンテキストを検出できません。AI によるセマンティックレビューを必ず実施してください。
このタスクの凍結済みの参照情報とファイルセットに対して、スキャナーで検出されなかった内容も含め、references/ai_semantic_review_prompt.md のプロンプトを使用してください。スキャナーの検出結果は確認の優先順位を決めるためのものであり、レビュー範囲を定めるものではありません。確認した項目、検出結果、未レビューの範囲を、公開リポジトリの外に記録してください。
スキャンとセマンティックレビューが完了し、その範囲に機密情報が何もないと判明した場合は、ここで終了してください。履歴を書き換えないでください。未完了の範囲は、問題なしではなく未検証のままです。
このステップを省略すると、gitleaks が検出対象として想定していなかった非公開コンテキストをプッシュしてしまう可能性があります。
ステップ 2:検出結果を分類し、修正方法を選択する
検出結果ごとに、次のいずれかを決定してください。
- 認証情報をローテーションする(有効なシークレットについては、必ず最初に実施してください)。
- 履歴から削除する(非公開のドメイン/IP アドレス、個人を特定できる情報(PII)、またはローテーション済みでも社内のコンテキストが漏れるシークレットが対象です)。
.gitignoreまたは許可リストに追加する(誤検出の場合のみ)。
確認された対象に応じて対応方法を選んでください。現在ホストされているテキストを修正する場合、または承認済みの PR 本文の改訂を反映する場合は github-ops を使用してください。ステップ 3~7 は、個別に許可された Git の書き換えにのみ使用してください。PR の改訂内容に対する承認は、ブランチやタグを書き換える許可ではありません。
有効なシークレットは、履歴をクリーンアップする前に必ずローテーションしてください。 履歴を削除しても、すでに漏えいしたシークレットは無効になりません。
対応方法を選ぶ前に、所有権に関する主張を検証してください。 対応方法は、そのリソースを誰が管理しているかによって異なります。自分が管理するフォーク/ブランチ/ドキュメントなら自分で削除できますが、第三者が管理するものなら GitHub Support への連絡、または所有者の協力が必要です。アカウント、フォーク、ドキュメントの所有者に関する監査レポートの注記は、どれほど確信をもって書かれていても未検証の主張として扱ってください。gh api repos/<owner>/<repo>、gh auth status を確認するか、ユーザーに尋ねてください。(2026-09-18:ある引き継ぎレポートでは、外部コントリビューターのフォークをユーザー自身のエージェントアカウントと記載していたため、対応方法が「サポートチケット」から「自分で削除する」へ、気付かれないまま変更されていました。実行時に失敗していたはずです。)
ステップ 3:置換ファイルを準備する
git-filter-repo の --replace-text 形式で、1 行につき 1 つの置換を記述したテキストファイルを作成してください。
literal:internal.example.com==>example.com
literal:private.example.org==>example.org
literal:sk-example-aaaaaaaaaaaaaaaa==>sk-example-REDACTED
これらを実際の機密文字列に置き換えてください。実際の値はコミットしないでください。置換ファイルはリポジトリの外に保管してください。
文字列の完全一致には literal: を使用してください。正規表現による置換には regex: を使用してください(パターンに確信がある場合に限ります)。
このファイルは、たとえば /tmp/sensitive-replacements.txt のように、リポジトリの外に保存してください。
ステップ 4:公開先を保存してから、バックアップを作成して書き換える
書き換える前に、既存のタスク状態に次の確認済みの値をリポジトリの外に保存します。選択したリモート名と確認済みのトランスポート設定、完全修飾された GitHub のターゲット(HOST/OWNER/REPO)、宛先ブランチの正確な ref、およびその完全なリモートコミット SHA です。認証情報はこの状態に含めず、設定済みの認証情報の仕組みをそのまま使用します。設定された push URL と URL 書き換えを含め、選択したリモートの push URL を 1 つ解決します。認証情報を含む URL は、別のコマンドに渡したり保存したりする前に拒否します。認証は既存の認証情報の仕組みを使用します。ブランチを照会する前に、認証情報を含まない確認済み URL が変更されずに解決されることを確認します。解決結果が変わった場合は不明として、スナップショットを中止します。単純な ls-remote origin では、異なる fetch URL が使われる可能性があります。
git -C /path/to/repo remote get-url --push --all origin
git -C /path/to/repo ls-remote --get-url '<verified-push-url>'
git -C /path/to/repo ls-remote --refs '<verified-push-url>' refs/heads/main
確認済みの push URL が 1 つだけであること、および ref の照会が成功し、一致する完全な SHA/ref の行が 1 行だけであることを必須とします。ブランチが存在しない場合や照会に失敗した場合は、書き換え前の値として保存できません。書き換えられたローカルブランチや古いリモート追跡 ref からこの値を導出しないでください。このタスクを再開する時点ですでに書き換えが済んでいる場合は、保存済みのターゲットと書き換え前の値を再利用します。状態が欠落している場合は push を中止します。
--yes は、表示された書き換えターゲットとバックアップパスを確認するためのものです。共有履歴の書き換えを許可したり、git-filter-repo 本来の新規クローンに関するチェックを回避したりするものではありません。元のチェックアウトに保留中の作業を保持し、必要に応じて以下の独立したクローンの手順を使用してください。
uv run scripts/rewrite_history.py --repo /path/to/repo \
--replacements /tmp/sensitive-replacements.txt \
--backup /tmp/repo-backup.bundle \
--yes
# Entity leaks live in commit MESSAGES too, not just file content. Cover both:
uv run scripts/rewrite_history.py --repo /path/to/repo \
--replacements /tmp/sensitive-replacements.txt \
--message-replacements /tmp/sensitive-replacements.txt \
--backup /tmp/repo-backup.bundle \
--yes
このスクリプトは次の処理を行います。
- 独立したリポジトリであり、紐付けられたワークツリーがないことを確認します。
git-filter-repoがインストールされ、実行可能であることを確認します。通常のクローンの場合は、クリーンステータスの照会が成功することを必須とします。ベアミラーの場合は、ワークツリーのないレイアウトが確認済みであることを使用します。照会に失敗した場合は書き換えを中止します。- 現在の状態のバックアップとして
git bundleを作成します。 git bundle verifyでバックアップバンドルを検証します。- 本来の安全性チェックを回避せずに
git filter-repo --replace-textを実行します。--message-replacementsが指定されている場合は、--replace-messageも実行してコミットメッセージを書き換えます。ファイルの blob だけを書き換えるのではありません。blob だけを対象にしたクリーンアップでは、コミットメッセージにエンティティ名が残る可能性があります。 - 変更前と変更後のコミットハッシュを報告します。
バックアップまたは検証の手順に失敗した場合、スクリプトは停止します。 手動で続行しないでください。
ステップ 5:クリーンアップを検証する
uv run scripts/verify_cleanup.py --repo /path/to/repo --replacements /tmp/sensitive-replacements.txt
これによりスキャナーを再実行し、元の機密文字列がどのコミットにも残っていないことも確認します。何かが見つかった場合は、ステップ 3 に戻ります。同じタスクの範囲内で、書き換え後の refs に対してステップ 1.5 を繰り返してから push してください。パターンチェックに成功しただけでは、意味的な検証が完了したことにはなりません。両方のチェックに合格したら、選択したブランチについて検証済みの正確なローカルコミットを解決して保存します。
git -C /path/to/repo rev-parse refs/heads/main^{commit}
その後にローカルで変更を加えると、この検証との紐付けは無効になります。新しい検証済みローカル SHA を選ぶ前に、変更された候補を再確認してください。
Step 6: 可視性を確認してプッシュする
git-filter-repo は origin を削除する場合があります。保存済みの名前付きリモートがない場合は、以前に確認したトランスポート設定と既存の認証情報の仕組みを使って復元してください。URL や認証情報を推測してはいけません。リモートが存在していても保存済みの内容と異なる場合は、作業を中止して対象を確認してください。ローカルソースからクローンした場合でも、そのローカルソースの URL が許可された GitHub の宛先になるわけではありません。
保存済みの公開先と古いリモート SHA、さらに Step 5 で確認したローカル SHA を指定します。通常のフックが引き続き実行されるよう、リモート名はそのままにしてください。
uv run scripts/safe_push.py --repo /path/to/repo --remote origin --branch main \
--expected-repository '<saved-host/owner/repo>' \
--expected-remote-sha '<saved-full-remote-sha>' \
--verified-local-sha '<verified-full-local-sha>' \
--yes
このスクリプトは次の処理を行います。
- 選択したプッシュ URL を保存済みのリポジトリの対象に結び付け、その対象の可視性とフォークのメタデータを検証します。
- リポジトリが公開されていてフォークが存在する場合は、明確に警告します。
- ローカルの候補が確認済みの SHA と一致することを確認します。
- 保存済みのリモートブランチに対し、期待するコミットを明示したリースを指定して、1 回だけプッシュします。リースまたはフックによって拒否された場合は停止し、強制プッシュには切り替えません。
--no-verifyの追加を拒否します。
PII Guard フックで失敗した場合は、問題を修正して再実行してください。回避してはいけません。
移行:--repo、--remote、--branch のみを指定する呼び出し元は、フェイルクローズで失敗するようになりました。書き換え前にリモートの対象と書き換え前の状態を取得し、上記のバインド引数をすべて追加してください。--yes を指定しても、欠けている証拠を補うことはできません。既存のバックアップ、置換、任意のメッセージ置換の各引数は引き続き使用できます。
Step 7: プッシュ後の検証
プッシュが成功した後:
- 公開された正確な refs と、書き換えの対象となった名前付きコンテンツを読み戻して確認します。ローカルの候補を検証しただけでは、ホスト上のオブジェクトが変更された証明にはなりません。
- 影響を受けるオープン PR が引き続き有効なコミットを対象としていることを確認します。履歴の書き換えによって各ブランチが壊れる場合があります。無関係な PR を修復したり、その変更を暗黙に破棄したりしてはいけません。
- Step 0.5 で記録した露出箇所を再確認します。Git の検証結果は、PR のリビジョンのクリーンアップ、キャッシュされたオブジェクトへのアクセス、サードパーティのコピーとは分けて報告してください。利用できない確認項目は、Git スキャナーの結果がクリーンでも未検証のままにしてください。
同梱スクリプトの動作
scripts/scan_repo.py
gitleaks と、gitleaks ではカバーされないパターン(プライベートドメイン、内部 IP、中国の電話番号、特定の PII)を検出するカスタムの bash/grep レイヤーを実行します。JSON レポートを出力します。
uv run --with gitpython scripts/scan_repo.py --repo /path/to/repo --output /tmp/report.json
scripts/rewrite_history.py
バックアップバンドルを作成し、git filter-repo --replace-text を実行します。コミットメッセージも --replace-message で書き換えるには、--message-replacements <file> を指定してください(通常、同じ置換ファイルで両方を処理できます)。
uv run --with gitpython scripts/rewrite_history.py \
--repo /path/to/repo \
--replacements /tmp/sensitive-replacements.txt \
--message-replacements /tmp/sensitive-replacements.txt \
--backup /tmp/repo-backup.bundle \
--yes
scripts/verify_cleanup.py
スキャナーを再実行し、すべてのコミットを対象に元の機密文字列を grep します。これにより、すべてのコミットに対する blob の内容(各コミットでの git grep)とコミットメッセージ(すべての refs を対象とした git log、ハッシュ付きレコード形式)の両方がカバーされるため、--replace-message を指定し忘れた書き換えも検証で失敗します。
uv run --with gitpython scripts/verify_cleanup.py \
--repo /path/to/repo \
--replacements /tmp/sensitive-replacements.txt
scripts/safe_push.py
可視性を確認し、安全にプッシュします。
uv run --with gitpython scripts/safe_push.py --repo /path/to/repo --remote origin --branch main \
--expected-repository '<saved-host/owner/repo>' \
--expected-remote-sha '<saved-full-remote-sha>' \
--verified-local-sha '<verified-full-local-sha>' \
--yes
特殊なケースへの対応
リポジトリにオープン PR がある場合
履歴を書き換えると、オープン PR のコミット refs は無効になります。プッシュ後:
- PR の作成者に、新しい
mainをベースに各ブランチをリベースするよう依頼します。 - PR が自分のものである場合は、ローカルブランチを削除し、書き換え後の
mainをフェッチして、変更を新しいコミットとして cherry-pick します。
リポジトリにフォークがある場合
公開フォークには、所有者が同期するまで古い履歴が残ります。リスクの高い漏えい(有効なシークレット、本番環境の認証情報)の場合は、次の対応を検討してください。
- 直ちに認証情報をローテーションします(必須)。
- 機密データのキャッシュ済みビューの削除を GitHub Support に依頼します。
- 簡潔で事実に基づくメッセージでフォークの所有者に通知します。
Support への依頼とフォーク所有者への連絡は、github-ops の承認契約に従って行ってください。書き換えの承認だけでは、これらの対応は許可されません。GitHub Support が受け付けるかどうかは条件付きであり、完全な削除が保証されるものではありません。公式の削除制限を参照してください。リスクが低い場合は、残存または未検証の露出と、選択した対応範囲を記録してください。書き換えを完全消去として報告してはいけません。
git filter-repo が「need a fresh clone」と報告する場合
ネイティブの拒否動作を維持してください。元のチェックアウトはそのまま保持し、新しい独立したミラーを作成します。--no-local を指定すると、ローカルクローンでオブジェクトを共有するショートカットを回避できます。
git clone --mirror --no-local /path/to/repo /tmp/repo-mirror.git
クローンの成功後、Skill のディレクトリから、同梱の rewrite_history.py を --repo /tmp/repo-mirror.git を指定して実行します。この方法を実行する前に、承認済みの公開先/変更前の状態を保存し、書き換えが終わるまで保持してください。新規クローンのガードを回避するために --force を追加したり、この条件を満たすために別のセッションの作業を削除したりしてはいけません。
gitleaks の誤検知
gitleaks がドキュメントの例やテスト用フィクスチャにフラグを立てた場合は、リポジトリの .gitleaks.toml または .gitleaksignore に許可リストのエントリーを追加してください(--no-verify は決して使用しないでください)。許可リストのパターンについては、references/tooling_notes.md を参照してください。
この Skill が行わないこと
- 有効な認証情報を代わりにローテーションすることはありません。まずローテーションし、その後に履歴をクリーンアップしてください。
- GitHub 自体のバックアップやフォークからデータを削除することはありません。クリーンアップの対象は上流リポジトリの履歴のみです。
- git フックを回避することはありません。フックが失敗した場合は、根本原因を修正してください。
- シークレットの漏えいを「安全」にすることはありません。一度プッシュしたデータは、見られたものとして扱ってください。
参考資料
references/incident-lessons.md— 実際のクリーンアップで起きた問題と、この Skill がそうしたミスを防ぐ方法。references/tooling_notes.md—git-filter-repoと BFG の使い分け、許可リストのパターン、よくあるエラー。references/ai_semantic_review_prompt.md— 正規表現では見つけられない非公開コンテキストを検出するための、レイヤー 4 の AI セマンティックレビュー用プロンプト。