OSS · Bash CLI · Git Hooks

private-lint

git履歴は忘れない。だから、入る前に止める。

個人名・プライベートな名前 (家族名、私用メール、契約先・勤務先ドメイン) の混入を commit / push の前に検出するgitフックゲート。pure Bash・依存ゼロ。検出パターンはマシンローカルにだけ置き、どのrepoにもcommitしない。repoをpublicにする直前の全履歴一括監査 (audit) も同梱。

GitHub で見る 実名が漏れる7つの経路 →

ターミナルデモ: 個人名と私用メールを含むcommitがpre-commitフックでblockされ、3件のパターンヒットがファイル名・行番号つきで列挙される。該当行を削除すると同じcommitが通る。デモ内の名前はすべて架空。
(架空の) 個人名と私用メールが入ったstagedファイル: commitはblockされ、3件のヒットが内訳表示されます。行を消せば同じcommitが通ります。

実名が漏れる7つの経路

「diffを検査すれば終わり」ではありませんでした。手持ちの公開repo 25個への展開で、diff検査には見えない経路から実物の混入が見つかっています。経路ごとに置いたゲート:

経路 ゲート 見るもの
stagedの差分 + ファイル名 pre-commit 追加行とstagedパス名 (人名で保存したスクリーンショットはファイル名だけで漏れる)
コミットメッセージ commit-msg メッセージ本文の各行。「山田さんの指摘を修正」は履歴になる前に止まる
pushされる範囲の差分 pre-push リモート未到達コミットの差分+ファイル名。新規ブランチはどのリモートにも未到達の全コミット
push先そのもの pre-push remote名とURL。repo名に契約先名が入ったままだと、中身をいくら検査しても素通りする
author identity pre-commit gitが解決した実効author/committer emailを許可リストと照合。未設定も違反扱い
既に履歴に入っているもの audit (手動) 全履歴の差分・全ファイル名・repo名・remote URL・現HEADを、公開前に一括で見る
例外機構の迂回口 設計 allow ルール・行内マーカー・audit baseline の適用範囲を、それぞれ1段狭く絞る

インストール

cloneして対象repoに向けるだけです。install は冪等で、既存hookの先頭に自分を挿入するので、古いhookの早期exitに迂回されません。install-template を打てば init.templateDir 経由で以後の全clone/initにも自動で入ります。

git clone https://github.com/kenimo49/private-lint.git
cd private-lint

mkdir -p ~/.config/private-lint
$EDITOR ~/.config/private-lint/patterns    # 1 ERE per line; never committed anywhere

./private-lint install ~/repos/<repo>      # idempotent; inserts itself before existing hooks
echo "mode=block" > ~/repos/<repo>/.privatelintrc   # public repos: block

./private-lint install-template            # auto-install into every future clone / init

使い方

どのフックも手動で実行でき、audit が公開前の一括監査です:

./private-lint check --staged                  # pre-commit, by hand
./private-lint check --message <file>          # commit-msg, by hand
./private-lint check --push-dest <name> <url>  # remote name / URL
./private-lint audit ~/repos/<repo>            # full history + repo name + remote + HEAD
./private-lint status                          # effective mode, hooks, pattern count

PRIVATE_LINT=off git commit ...                # intentional bypass

4つの設計判断

面白い判断はぜんぶ「各機構をどちら側に倒すか」に集まっています。

  1. ゲートは素通しに、監査はエラーに倒す

    パターン未設定のマシンでは、フックは全部素通しします。配布前からblockするallowlistは、届いた先のマシンを全部止めてしまうからです。audit はこれを逆に倒し、パターン未設定なら非0で終了します。パターン0件のまま「監査した」が成立するのが、このツールが出しうる最悪の出力だからです。

  2. identities は denylist ではなく allowlist

    patterns は「出てはいけないもの」を書き、identities は「commitしてよいauthor email」を書きます。逆転している理由は、user.email 未設定のrepoで commit するとgitがグローバル値や自動検出値を静かに継承するからで、私用アドレスが公開履歴に入る事故はまさにそこで起きます。

  3. 7経路のうち3つは、展開中に実物を検出した

    push先チェック・identityチェック・全履歴audit は、既存repo群への導入作業の最中にそれぞれ実物の混入をフラグしました。想定上の防御ではありません。

  4. 例外機構は1段狭く絞る

    行内マーカー (private-lint:allow) はファイル名やremote URLなどの生成メタデータ行には効かず、本来カバーすべきでない欄に持ち込めません。audit baseline が黙認できるのは履歴のヒットだけ。現在のrepo名やremote URLのヒットは鳴り続けます。それは過去の受容済み検出ではなく、現在進行形の露出だからです。

関連する開発ツール

プロダクト一覧 →

← プロダクト一覧へ