OSS · Bash CLI · Git Hooks
private-lint
git履歴は忘れない。だから、入る前に止める。
個人名・プライベートな名前 (家族名、私用メール、契約先・勤務先ドメイン) の混入を commit / push の前に検出するgitフックゲート。pure Bash・依存ゼロ。検出パターンはマシンローカルにだけ置き、どのrepoにもcommitしない。repoをpublicにする直前の全履歴一括監査 (audit) も同梱。
実名が漏れる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つの設計判断
面白い判断はぜんぶ「各機構をどちら側に倒すか」に集まっています。
-
ゲートは素通しに、監査はエラーに倒す
パターン未設定のマシンでは、フックは全部素通しします。配布前からblockするallowlistは、届いた先のマシンを全部止めてしまうからです。audit はこれを逆に倒し、パターン未設定なら非0で終了します。パターン0件のまま「監査した」が成立するのが、このツールが出しうる最悪の出力だからです。
-
identities は denylist ではなく allowlist
patterns は「出てはいけないもの」を書き、identities は「commitしてよいauthor email」を書きます。逆転している理由は、user.email 未設定のrepoで commit するとgitがグローバル値や自動検出値を静かに継承するからで、私用アドレスが公開履歴に入る事故はまさにそこで起きます。
-
7経路のうち3つは、展開中に実物を検出した
push先チェック・identityチェック・全履歴audit は、既存repo群への導入作業の最中にそれぞれ実物の混入をフラグしました。想定上の防御ではありません。
-
例外機構は1段狭く絞る
行内マーカー (private-lint:allow) はファイル名やremote URLなどの生成メタデータ行には効かず、本来カバーすべきでない欄に持ち込めません。audit baseline が黙認できるのは履歴のヒットだけ。現在のrepo名やremote URLのヒットは鳴り続けます。それは過去の受容済み検出ではなく、現在進行形の露出だからです。
関連する開発ツール
- hook-chain-lens Claude Codeのhook群(user / project / local / plugin)を全部読んで、今このcwdで実際にどう発火するかを出す読み取り専用CLI。
- quake-lens 地震統計のCLI+MCPサーバー。標準ライブラリだけのPythonで、公開カタログからb値 (Aki最尤推定) と大森・宇津則の余震減衰 (Ogata最尤推定) を計算する。
- rhythm-lens 日本語・英語・ポルトガル語Markdownのリズム (文長の揺れ・段落構造・burstiness) を、2本の論文の実測分布とパーセンタイル照合するCLI。AI検出器ではなく、執筆フィードバックの計測器。
- claude-shift 複数のClaude Codeアカウントを管理するツール。activeアカウントをCLIから切り替えつつ、Claude Codeが実際に読んでいる2ファイル認証(~/.claude/.credentials.json + ~/.claude.json)を両方同期。5時間枠と週次の使用率をアカウント横断でChrome拡張から見られる。同じUIはブラウザとTauri v2デスクトップアプリでも動く。