
スクリーンショットから認証情報を見つけ、シークレットマネージャーに保存し、画像から不可逆的に墨消しします — ローカル、オフライン、OCRベース。
スクリーンショットから認証情報を見つけ出し、シークレットマネージャーに抽出し、画像から不可逆的に編集(redact)します。これにより、シークレットがノートやスクリーンショット、~/Desktop に放置されるのを防ぎます。
ローカル。オフライン。サイレント。デフォルトではネットワークもテレメトリもありません。
screenscrub は既存の2つのツールカテゴリをつなぎます。GUI 編集ツール(Xnapper、Shhshot、macshot)は手動でPIIをぼかしますが、シークレットと住所の区別がつかず、何を隠したかも記録しません。シークレットスキャナー(gitleaks、TruffleHog)は実際の認証情報を見つけますが、テキストのみであり、PNG内では検出できません。screenscrub は、gitleaks レベルの検出にレッドチームパックとエントロピー分析を加え、ヘッドレスでピクセルに適用します。各シークレットをマネージャーに取り込み、画像を不可逆的に編集し、結果を再OCRしてシークレットが削除されたことを証明します。
セーフティネットであり、保証ではありません。screenscrub は画像が「確実にクリーン」であるとは決して主張しません。OCRと検出はどちらも見落としがあるため、常に結果を目視確認してください。これに依存する前に読んでください。
screenscrub scan ~/Screenshots # ドライラン:検出+報告、書き込みなし
screenscrub clean ~/Screenshots --out ~/clean \ # 抽出、編集、検証、隔離
--sink file://./secrets.age --yes
screenscrub verify ~/clean # フォルダがクリーンであることを証明(違えば終了コード≠0)
screenscrub clean --keep-originals ./tmp
Cleaning 7 image(s) in ./tmp (7 worker(s))…
[1/7] tmp/ssh.jpg — 6 region(s) → tmp/ssh.redacted.jpg ✓ verified
[2/7] tmp/key.jpg — 13 region(s) → tmp/key.redacted.jpg ✓ verified
[3/7] tmp/test2.jpg — 12 region(s) → tmp/test2.redacted.jpg ✓ verified
[4/7] tmp/test5.jpg — 13 region(s) → tmp/test5.redacted.jpg ✓ verified
[5/7] tmp/test3.jpg — error: verification failed: 4 secret region(s) still detectable in tmp/test3.redacted.jpg; original preserved
[6/7] tmp/test4.jpg — error: verification failed: 9 secret region(s) still detectable in tmp/test4.redacted.jpg; original preserved
[7/7] tmp/test6.jpg — error: verification failed: 7 secret region(s) still detectable in tmp/test6.redacted.jpg; original preserved
7/7 file(s) flagged, 382 region(s) total, 3 file(s) errored/unverified.
By kind:
high-entropy 343
keyword-secret 15
db-connection-uri 10
github-pat 6
unix-crypt-hash 6
aws-access-key 1
stripe-key 1
Needs manual attention (3):
tmp/test3.jpg — error: verification failed: 4 secret region(s) still detectable in tmp/test3.redacted.jpg; original preserved
tmp/test4.jpg — error: verification failed: 9 secret region(s) still detectable in tmp/test4.redacted.jpg; original preserved
tmp/test6.jpg — error: verification failed: 7 secret region(s) still detectable in tmp/test6.redacted.jpg; original preserved
| Before — シークレットの値が見える状態 | After clean — 不可逆的に編集 |
![]() | ![]() |
同じスクリーンショットのclean前後:すべてのシークレット値がピクセルにデコードされ、矩形で囲まれ、新しい画像に再エンコードされます(不可逆)。ラベルと構造は読み取り可能なままです。
tesseract OCR バイナリが PATH 上にある必要があります。
brew install tesseractwinget install UB-Mannheim.TesseractOCR(または UB Mannheim インストーラ)apt-get install tesseract-ocrその後ビルド:
go build -o screenscrub ./cmd/screenscrub
クロスコンパイル可能: macOS (arm64/amd64) および Windows (amd64) — cgo ではなく tesseract を外部呼び出しします。macOS でのみテスト済み。
scan <dir|file> — ドライラン検出して報告します。何も書き込みません。
screenscrub scan ~/Screenshots --ignore 'node_modules/**' --ignore '*.thumb.png'
screenscrub scan shot.png --json # 機械可読レポート
screenscrub scan ~/Screenshots --preview ~/review # clean が何を編集するか目視確認
--preview <dir> は、フラグが付いた各画像の非破壊コピーを、検出された領域をアウトライン(塗りつぶしなし)で書き込みます。これにより、clean が何を編集するかをコミット前に確認できます。⚠ これらのコピーにはシークレットがまだ含まれているため、ディレクトリは 0700 で作成され、共有してはいけません。
レポートは集計で終わります。種別ごとの内訳と、明示的な「手動確認が必要」リスト — エラー、検証失敗、またはスクリーンショットであるべき画像から OCR テキストが一切得られなかった ファイルが含まれます(サイレントな OCR の見落としはクリーンな画像と区別がつかないため、信頼するのではなく表面化されます)。
scan --fail-on-findings は何か検出された場合にゼロ以外で終了します — pre-commit/CI ゲートとして。
clean <dir|file> — 抽出、編集、隔離SCREENSCRUB_PASSPHRASE=… screenscrub clean ~/Screenshots \
--out ~/Screenshots/clean \
--sink file://./secrets.age \
--yes
検出結果がある各画像に対して、clean は:
<name>.redacted.png を --out に(または元の場所の隣に)書き込み、<out>/quarantine/ に age 暗号化して移動します。シークレットは --sink に指定したシンクに送られます: age ファイル(デフォルト)、1Password(op://)、KeePassXC(keepassxc://)、macOS キーチェーン(keychain://)、HashiCorp Vault(vault://)— またはハードウェアベースの age 受信者(YubiKey、Touch ID)。シークレットシンク を参照。
フラグ:
--exclude-kind と --min-confidence は、ノイズの多いコーパス用のローカル(ネットワーク不要)精度調整つまみです。--llm のオフライン版で、リコールを犠牲にしてレポートを静かにします。意図的に使用してください。どちらも scan/verify で動作し、--config(exclude_kinds、min_confidence)でも設定可能です。
パスフレーズの解決順序: --passphrase、設定ファイル、SCREENSCRUB_PASSPHRASE。コマンドラインでの --passphrase は避けてください — argv は他のプロセスから見えます。
verify <dir|file> — フォルダがクリーンであることを証明各画像を再OCRし、検出を再実行し、シークレット領域がまだ見つかる場合はゼロ以外で終了 — スクリーンショットをクライアントに送る前の納品/CIゲートとして使用します。(OCR がシークレットを読めないことを証明するものであり、人間が決して読めないことを保証するものではありません。)
screenscrub verify ~/deliverable/screenshots
echo $? # 0 = クリーン、非ゼロ = シークレットがまだ検出可能
clean はこの同じチェックをすべての編集済みコピーに対して自動的に実行し、編集済みコピーが検証に失敗した場合は元のファイルを隔離またはシュレッドしません。--no-verify でスキップ可能(非推奨)。
open <store> — シークレットまたは隔離された元ファイルを復元データを取り戻す: open は age シンク(保存されたシークレットレコード)または隔離された元ファイルを復号化し、--out に書き出します。stdout には決して出力しません。ストアの復号化 を参照。
--llm — LLM を使用して良性の過剰検出を削除OCR テキスト(OCR ノイズあり)に対する gitleaks レベルのルールは必然的に過剰検出します。リコール重視の検出器は、オペレーターが保持したい良性の高エントロピー文字列(ホスト名、GUID、git SHA、NTLM ハッシュなど、それ自体が侵害の証拠であるもの)を表面化します。--llm は各候補を Claude(Opus 4.8)に渡し、本当のシークレットを保持し、良性の識別子を削除します。LLM が確認した領域は赤く塗りつぶされます。
ANTHROPIC_API_KEY=… screenscrub clean ~/Screenshots --out ~/clean --llm --yes
これは意図的にローカル・サイレントを破ります。候補文字列(検出されたシークレット)を Anthropic API に送信するため、オプトインのみ、警告を表示し、API キーが必要です。フェイルクローズ: API/ネットワークエラーが発生した場合は、すべての検出結果を保持(すべてを編集)し、本当のシークレットを削除するリスクを避けます。フラグが付いた画像ごとに1回の API 呼び出しを行います(--jobs に関係なく最大4並行)— 大規模なディレクトリではレート制限とコストに注意。
--watchscreenscrub clean ~/Screenshots --watch --sink file://./secrets.age --yes
既存のすべてを処理し、その後新しい/変更された画像を監視し、現れるたびに編集します。*.redacted.png 出力はループを避けるために無視されます。
OCR トークンに対して、screenscrub は以下を組み合わせます:
AKIA…、GitHub ghp_…、GitLab、Slack、Stripe、OpenAI、GCP API キー、JWT、PEM ヘッダー。(インポートではなくコピーで、軽量かつ完全オフラインを維持。)postgres://u:p@…)、HTTP Basic/Bearer 認証ヘッダー、NetNTLMv2 および NTLM(pwdump)ハッシュ、/etc/shadow crypt ハッシュ、PuTTY および OpenSSH 秘密鍵、SendGrid/npm トークン。password、token=、secret、api_key、…)— パターンにマッチせずエントロピーが低い場合でも隣接する値にフラグを立てます。BEGIN/END マーカーを手がかりに、該当領域全体を寛容に編集します(単語ごとのマッチングは信用しません)。各ヒットは、それを構成するトークンのピクセル矩形にマッピングされます(2つの行再構成 — スペースありと密 — で OCR が空白で分割したシークレットを復元)。矩形は塗りつぶし前にパディングされます。
シンクは1つのメソッド: Store(Secret) error。実装済みアダプター:
file:// — age 暗号化ローカルファイル(デフォルト)。 外部依存無し; JSON 配列のレコードを age scrypt パスフレーズ(または age 受信者 — 後述のハードウェアベース暗号化参照)で暗号化。op:// — 1Password CLI。 値を stdin で op item create に渡します(argv には決して含まれません)。keepassxc://./vault.kdbx — KeePassXC。 人気のクロスプラットフォームローカル KeePass マネージャー。keepassxc-cli add を実行; データベースパスワードとシークレットはともに stdin で渡されます(argv には決して含まれません)。データベースパスワードは解決されたパスフレーズ(--config/SCREENSCRUB_PASSPHRASE)です。keychain://screenscrub — macOS キーチェーン。 security add-generic-password を実行。注意: security ツールはシークレットをコマンドライン引数としてのみ受け付けるため、実行中は同じユーザーの ps から一瞬見えます(macOS は他のユーザーからは argv を隠します)。より厳格な衛生管理には、1Password、KeePassXC、または以下の Secure Enclave 受信者を推奨。vault:// — HashiCorp Vault KV v2 — 存在するが延期/あまり一般的ではない; パスはシークレットをハッシュ化するため、URL にシークレットが現れることはありません。独自のものを追加するには、sink.SecretSink(internal/sink)を実装し、sink.FromURI に配線します。それが契約のすべてです。
デフォルトの age ファイルシンクと隔離は、パスフレーズの代わりに age 受信者(ハードウェアベースのものを含む、age プラグイン経由)に暗号化できます:
# YubiKey (PIV) — age-plugin-yubikey をインストール後:
screenscrub clean ~/Screenshots --out ~/clean \
--sink file://./secrets.age \
--recipient age1yubikey1q... --yes
# Apple Secure Enclave / Touch ID — age-plugin-se をインストール後:
screenscrub clean ~/Screenshots --out ~/clean \
--recipient age1se1q... --yes
# 通常の age 鍵ペア、または複数の受信者 / 受信者ファイル:
screenscrub clean ~/Screenshots --recipient age1abc... --recipients-file team.age.pub --yes
受信者モードではパスフレーズは不要。シンクと暗号化された隔離の両方が受信者に向けて暗号化されます。ハードウェアトークンで復元: age -d -i <identity> secrets.age(復号化時に YubiKey タッチ / Touch ID を要求)。プラグイン受信者への暗号化には age-plugin-<name> バイナリが必要。復号化にはさらにデバイスが必要。
openシンクと隔離は、1つのパスフレーズから派生した別々の鍵(HKDF)を使用するため、一方のストアをクラックしても他方を開くことはできません。ディスク上の鍵は派生されているため、生の age ではなく screenscrub で復号化します:
screenscrub open ./secrets.age --out secrets.json # シークレットレコード
screenscrub open ~/clean/quarantine/shot.png.ab12cd34.age --out shot.png # 元ファイル
open は自動的に各派生鍵を試し、平文を --out に書き込みます(stdout には決して出力しません)。受信者モードのストアには、age 鍵ファイルを指定: screenscrub open secrets.<id>.age --identity key.txt --out secrets.json(YubiKey/Touch ID の場合は age -d -i を直接使用。これによりハードウェアプラグインが駆動されます)。受信者モードではシークレットごとに1つのファイル(secrets.<hash>-<rand>.age)を書き込むため、繰り返し実行しても上書きされません。
go test ./... # ユニットテストは tesseract なしで実行; OCR/再OCRテストは tesseract がない場合はスキップ
主要な保証はエンドツーエンドテストでカバーされています。AWS キーを含むスクリーンショットをレンダリングし、実際の OCR → 検出 → 編集パイプラインを実行し、出力を再OCRし、シークレットが削除されたことをアサートします(internal/redact)。
screenscrub は考えることをやめてよいという免許証ではありません。
password: 行など)は認識しますが、認識しない形状のシークレットは通過します。再現率を優先して調整されています。意図的に過剰検出します。なぜなら、画像に認証情報が残ることが重大なケースであり、誤検出は単に編集されたブロブが増えるだけのコストだからです。
実際のユースケースは testdata/ フォルダで確認できます。RExpository のスクリーンショットから取得したものです:
https://jaimepolop.github.io/RExpository/
完璧ではありませんが、かなり良いです。
clean はそれを再OCRし、検出を再実行します。シークレットがまだ検出可能な場合、元のファイルを隔離または破棄せず、ファイルにフラグを立てます。編集矩形はグリフの高さに比例してパディングされるため、大きな/高DPIテキストのアンチエイリアスエッジもカバーされます。clean は編集済みコピーを書き込み、検証し、その後、未編集の元ファイルを age 暗号化された隔離に移動し、元の場所から削除します — コピーがクリーンであると検証され、抽出されたシークレットが安全にディスクに保存された後にのみ行われます。破壊的なステップには --yes が必要です。AK****…**LE)のみが表示されます。唯一の平文コピーはメモリ内に存在し、暗号化されたシンクに到達するまで保持されます。--purge と安全な削除の限界デフォルトでは、元ファイルは(暗号化されて)隔離に保持されるため、誤った編集でも復元可能です。--purge は代わりに元ファイルをシュレッドします: ランダムで上書き、ゼロで上書き、その後 unlink。
安全な削除は信頼できません。SSD、コピーオンライトファイルシステム(APFS、Btrfs、ZFS)、ジャーナリングファイルシステム、スナップショット/バックアップされたボリュームでは、上書き→unlink は元のバイトが確実に削除されることを保証しません — ウェアレベリングやスナップショットが保持する可能性があります。唯一の本当の防御は、そもそも平文を永続化しないことです。暗号化された隔離を推奨し、--purge はベストエフォートとして扱ってください。
OCR インターフェースの背後にある、より高精度なバックエンドとして自然な選択肢です — 将来の追加候補。ベンダーコピーされた認証情報パターンは gitleaks (MIT) に由来します。
| フラグ | 意味 |
|---|
--out <dir> | 編集済みコピーの出力先(デフォルト: 元の場所の隣) |
--sink <uri> | file://./secrets.age · op://Vault · keepassxc://./v.kdbx · keychain://service · vault://KV |
--recipient <age1…> | age 受信者に暗号化(繰り返し可能): X25519、YubiKey、または Touch ID |
--recipients-file <f> | age 受信者を1行に1つ記載したファイル |
--passphrase | ファイルシンク / 隔離のパスフレーズ(環境変数または --config を推奨) |
--watch | 常時実行;新しいスクリーンショットが現れるたびに処理(fsnotify) |
--purge | 元のファイルを隔離ではなくシュレッド(以下の注意事項参照) |
--keep-originals | 編集済みコピーのみ書き込み;元のファイルはそのまま(隔離なし、パスフレーズ不要、--yes 不要)— --purge と排他 |
--yes | 元のファイルの破壊的処理を確認(必須) |
--config <file> | JSON 設定ファイル;パスフレーズを argv に含めずに済む |
--no-verify | 編集済み出力の再OCR検証をスキップ(非推奨) |
-i, --interactive | フラグが付いた各画像を開き、編集/スキップを確認してから実行 |
--llm | Claude を使用して良性の過剰検出を除去;候補文字列を Anthropic API に送信 |
--llm-api-key | --llm 用の Anthropic API キー(または ANTHROPIC_API_KEY) |
--jobs N | N個の画像を並列処理(デフォルト: CPU コア数) |
--manifest <file> | 元ファイル→編集済みファイル→領域の JSON マップを書き込む |
--ocr-raw / --ocr-upscale N | 暗い端末の前処理を無効化 / OCR 前にアップスケール |
--exclude-kind <kind> | 特定の種類の検出結果を抑制(繰り返し可能)。例: high-entropy — オフライン精度調整 |
--min-confidence <0..1> | この信頼度未満の検出結果を削除 — オフライン精度調整 |
--json | レポートを JSON で出力 |
--ignore <glob> | 無視するグロブ、繰り返し可能;** は任意の深さにマッチ |
--lang | tesseract 言語(デフォルト eng) |