画面共有で env を叩いてヒヤッとしたことがある
ペアプロ中に環境変数を確認しようと env を叩いた瞬間、相手の画面に AWS のアクセスキーが流れた経験はないでしょうか。docker logs に平文のトークンが混ざるのも、CI のジョブログに DB 接続 URL が出るのも、身に覚えのある人は多いはずです。
この「ターミナル出力経由でシークレットが漏れる瞬間」を塞ぐために、mask-pipe という Go 製の CLI を作りました。TruffleHog がリポジトリを守り、secretlint がコミットを守るのに対して、mask-pipe は ターミナル画面 を守る役割を担います。v0.1.1 をリリースしたので、設計と使い方をまとめておきます。
デモ
使い方はシンプルで、隠したい出力の先にパイプで繋ぐだけです。

$ env | mask-pipeAWS_ACCESS_KEY_ID=AKIA************MPLEAWS_SECRET_ACCESS_KEY=wJal********************************EKEYDATABASE_URL=postgres://admin:****@db.example.com:5432/mydbGITHUB_TOKEN=ghp_********************************ef01STRIPE_SK=sk_live_********************************p7dcDocker や Kubernetes のログもそのまま流せます。
$ docker logs -f my-app 2>&1 | mask-pipe$ kubectl logs pod-name | mask-pipe --show-tail 0本番運用のコマンドに噛ませる前に、何が置換されるかを確認したいときは --dry-run を使います。元の出力はそのまま、マスク対象があった行だけが報告される挙動です。
$ terraform plan | mask-pipe --dry-runインストール
静的ビルドのシングルバイナリとして配布しています。Homebrew・go install・バイナリ直ダウンロードのいずれでも入ります。
$ brew install c12o-dev/tap/mask-pipe$ go install github.com/c12o-dev/mask-pipe/cmd/mask-pipe@latestmacOS(Intel / Apple Silicon)、Linux(amd64 / arm64)、Windows(amd64)を公式ビルド対象にしています。依存ライブラリは一切なく、ランタイムも不要です。
組み込みパターンは 8 つだけ
v1 の時点では以下の 8 パターンだけを既定で有効にしています。
| ID | 検出対象 | マスク例 |
|---|---|---|
aws_access_key | AKIA + 16 文字の AWS アクセスキー ID | AKIA************MPLE |
aws_secret_key | aws_secret_access_key=... の値部分 | wJal****EKEY |
github_token | ghp_ / gho_ / ghu_ / ghs_ / ghr_ で始まるトークン | ghp_****1234 |
github_pat | github_pat_ で始まる fine-grained PAT | gith****BBcc |
stripe_key | sk_live_ / sk_test_ / pk_live_ / pk_test_ | sk_l****p7dc |
jwt | ドット区切り 3 セグメントの base64url | eyJh****sR8U |
db_url_password | scheme://user:pass@host のパスワード部分 | ://user:****@host |
pem_private_key | -----BEGIN ... PRIVATE KEY----- ブロック | [REDACTED PRIVATE KEY] |
全パターンの仕様は docs/specs/002-pattern-library.md に書きました。
設計上のいちばん大事なポリシーは 「再現率より精度を優先する」 ことです。通常の出力を壊す誤検知 1 件は、検出漏れ 10 件よりユーザーの信頼を損ないます。TruffleHog の 800 以上あるパターンをそのまま持ち込まなかったのは、誤検知率をコントロールしきれないと判断したためで、自分たちで精度 99% を保証できる 8 パターンから始めて少しずつ足していく方針にしています。
細かい話だと、Stripe キーの正規表現に \b アンカーを付けるかで悩みました。アンカーがないと disk_test_xxx のようなカラム名が誤検出されることをパターンレビュー用のエージェントが見つけたので、全パターンに単語境界を入れてあります。PEM 秘密鍵は単一行では一致できないので、ADR 0004 に書いた BEGIN/END マーカー方式のマルチラインバッファリング で実装しています(バッファ上限 100 行または 64KB を超えたら fail-open でそのまま流します)。
設定でカスタマイズ
~/.mask-pipe.toml を置くだけでパターンの切り替え、カスタムパターンの追加、allowlist、表示スタイルの調整ができます。
[builtin]jwt = false # JWT 検出をオフに
[[custom]]name = "internal-api-key"pattern = 'mycompany-key-[a-zA-Z0-9]{32}'
[[allowlist]]name = "aws-doc-example"pattern = 'AKIAIOSFODNN7EXAMPLE'
[display]mask_char = "*"show_tail = 4allowlist は「公式ドキュメントのサンプルキー」など、既知の安全な値をマスクしないためのエスケープハッチです。--mask-char '#' や --show-tail 0 のような CLI フラグでも同じ調整ができます。
設定の検証には mask-pipe doctor、有効パターンの確認には mask-pipe list-patterns を用意しました。トラブル時はまずこの 2 つを叩いてもらう想定です。
設計で悩んだところ
なぜ「パイプ専用」にしたのか
最初は 1Password の op run のように、シェルに仕込んでおけば全ての子プロセスの stdout を透過的にマスクするモードも検討しました。ただ、op run には TTY が潰れて TUI アプリが壊れる という有名な問題があります。同じ穴に落ちるのを避けるため、mask-pipe は 明示的にパイプで繋いだときだけ効く 設計にしました(ADR 0002)。色付き出力も対話プロンプトもそのまま動きます。PTY プロキシ方式の mask-pipe shell は v2 で再検討する予定です。
なぜ Go なのか
単一バイナリ配布が絶対条件で、OpenSSL の動的リンク事故を避けたかったので Go を選びました(ADR 0001)。標準の regexp パッケージは RE2 を採用しており、計算量が入力長に対して線形 であることが保証されます。ストリーミングで遅延を予測できるのは、リアルタイムに流れるログをフィルタする CLI にとってかなり重要でした。goreleaser で Homebrew・Scoop・APT を一気に配布できるのも楽です。
TruffleHog と競合にしない
比較表は README に詳しく書きましたが、要点だけ書くとこうなります。
| mask-pipe | TruffleHog | secretlint | GitHub add-mask | |
|---|---|---|---|---|
| 守る対象 | ターミナル出力 | Git リポジトリ | コミット前のファイル | CI ログ |
| 実行タイミング | リアルタイム(pipe) | 事後スキャン | pre-commit | CI 実行時 |
| ローカルで動く | ○ | ○ | ○ | ×(CI 専用) |
いずれも 補完関係 にあり、全部組み合わせるのが現実的な防御ラインだと考えています。
既知の限界
- TUI アプリはラップできません。 これはパイプで動かすことを選んだ時点での意図的な制約です。
vimやdocker run -itを巻き込んだ瞬間に操作不能になります。 - エントロピーベースの検出は v1 では採用していません。 Gitleaks 系の誤検知が許容しきれない領域だったので、パターンマッチ一本で割り切りました。
- PTY プロキシ(
mask-pipe shell)は v2 で検討中です。 「いちいち| mask-pipeと書くのが面倒」という声への答えですが、TTY 問題を再発させずに実装する道筋が見えてから着手します。
おわりに
公開場所は github.com/c12o-dev/mask-pipe です。brew install c12o-dev/tap/mask-pipe 一発で入るので、画面共有で一度でもヒヤッとしたことがあるなら試してみてください。フィードバックは GitHub Discussions で歓迎します。Star も貰えたら嬉しいです。