検出モデル
shk が検出結果を分類する仕組み: 重要度レベル、検出結果の種類、シークレットと PII のカバレッジ、アクションガード、MCP 監査、JSON 出力。
このページの内容
shk は、組み込みの正規表現ベースのルールと、任意で追加できるプロジェクト固有のカスタムルールを使ってテキストをスキャンします。検出結果には、ルール ID、重要度、種類、ファイルラベル、行、列、メッセージ、秘匿化された値、信頼度スコアが含まれます。
一致した生の値は JSON レポートには出力されません。redacted_value は [REDACTED] になります。
重要度レベル
| レベル | 例 |
|---|---|
critical |
秘密鍵の PEM ブロック。 |
high |
OpenAI 形式のキー、AWS アクセスキー ID、Anthropic キー、Google API キー、GitHub トークン、Slack トークン、Stripe キー、データベース URL、dotenvx の秘密鍵、すべての secret.gitleaks.* ルール、および平文の PII(個人情報)の隣で見つかった仮名化トークン。 |
medium |
JWT、Bearer トークン、汎用的な API キーの代入、メールアドレス、クレジットカード番号、電話番号、米国 SSN、および日本語の PII ルールの大半。 |
low |
IP アドレス、信頼度の低い PII パターン(EIN、郵便番号、パスポート番号、日本の銀行口座、健康保険証、住所のパターン)、および期限切れの許可リスト(allowlist)エントリなどのポリシー警告。 |
info |
スキップ通知、ラベルアンカー付きの個人名と英語の住所、およびその他の情報レベルの検出結果。 |
AI コンテキスト安全性のカバレッジ
AI コンテキストルールは [rules] で ai_context = true のときに実行され、種類 ai-context の検出結果を報告します。
デフォルトのルールセットは、AI から見えるコンテキストを改変または隠蔽しうる、シグナルの強い構文に焦点を当てています。
ai_context.unicode_tag_chars(high): Unicode タグ文字(U+E0000..U+E007F)。ai_context.bidi_control(ソースコードファイルではhigh、その他のテキストではlow): Trojan Source 型の視覚的並べ替えに関連する双方向制御文字。ai_context.embedded_bom(medium): ファイル先頭以外に現れるバイトオーダーマーク。ai_context.invisible_format_chars(high): ソフトハイフン、結合書記素接合子、アラビア文字マーク、モンゴル語母音区切り、ゼロ幅スペース、単語接合子などの不可視 Unicode 書式文字。絵文字や文字整形での一般的な誤検知を避けるため、ゼロ幅接合子/非接合子はデフォルトで除外されます。ai_context.variation_selector(medium): 視覚的な表示を変えうる補助 Unicode 異体字セレクタ(U+E0100..U+E01EF)。一般的な絵文字表示セレクタ(U+FE00..U+FE0F)はデフォルトで除外されます。ai_context.unsafe_uri(high、SVG data URI の場合はmedium): JavaScript スキームのリンク、およびtext/html、text/javascript、image/svg+xml、JavaScript のアプリケーションタイプなど、実行可能なdataURI メディアタイプ。
Markdown 画像による情報持ち出しや自然言語のプロンプトインジェクション文言など、信頼度の低い検出器はデフォルトのルールセットには含まれません。
検出結果の種類
ルールエンジンは次の種類をサポートします。
| 種類 | 用途 |
|---|---|
secret |
API キー、トークン、データベース URL、秘密鍵、およびそれに類する機密性の高い認証情報。 |
pii |
個人情報のパターン。 |
env |
環境変数関連のルールとヒント。組み込みの env.sensitive_assignment ルールは、機密性の高い変数名(*PASSWORD*、*PASSWD*、*SECRET*、*TOKEN*、*API_KEY*、*APIKEY*、*PRIVATE_KEY*、*ACCESS_KEY*、*CREDENTIAL*)に対する、プレースホルダーではない値の dotenv 形式の代入を検出します。env ルールは dotenv 形式のファイル(ファイル名が .env で始まるか .env で終わるもの)にのみ適用されるため、環境変数を読み取るソースコード(例: DB_PASSWORD = os.environ[...])は検出されません。.env.example と .env.sample ファイルはスキップされ、[rules] env = false でこの種類を無効化できます。構造上、公開または非シークレットである名前は除外されます。ブラウザビルド用プレフィックス(NEXT_PUBLIC_、VITE_、REACT_APP_、EXPO_PUBLIC_、GATSBY_、NUXT_PUBLIC_、VUE_APP_、PUBLIC_)はクライアントバンドルにインライン化されるため除外されます(ただし名前に SECRET/PASSWORD/PASSWD/PRIVATE も含まれる場合は設定ミスとして報告されます)。また *_PATH/*_FILE/*_DIR の名前はシークレットそのものではなく場所を保持するため除外されます。ベンダー形式の secret.* ルールは、名前とは無関係に値の照合を継続します。同じ公開名の除外は、あらゆるファイル種別において secret.generic_api_key にも適用されます。 |
ai-context |
AI コンテキスト向けのルール。 |
ignore |
スキャナーのスキップ通知およびポリシー警告。 |
internal |
[[custom_rules]] エントリのデフォルトの種類で、[rules] internal_terms によって制御されます。カスタムルールは他の任意の種類文字列を宣言できます。 |
git |
Git 関連の検出結果のために予約されています。現在、この種類を出力する組み込みルールはありません。 |
mcp |
shk mcp audit が生成する MCP サーバー設定の検出結果。 |
数字のみの値が無視されるのは、有効期限、TTL、ポート、タイムアウト、タイムスタンプ、バージョンなどの メタデータ名の場合に限られます。数字のみの認証情報は引き続き報告対象です。
すべての種類がすべてのコマンドや組み込みルールセットで使われるわけではありません。
組み込みシークレットのカバレッジ
組み込みのシークレットルールは、2 つのソースを組み合わせています。
crates/shk-rules/src/lib.rsにある手動調整済みのshkルール。crates/shk-rules/src/gitleaks_rules.rsにある、gitleaks のデフォルト設定から適応して生成されたsecret.gitleaks.*ルール。
手動調整済みの shk ルールには、次のパターンが含まれます。
- OpenAI 形式の API キー。
- AWS アクセスキー ID。
- Anthropic API キー。
- Google API キー。
- GitHub トークン。
- Slack トークン。
- Stripe API キー。
- Hugging Face トークン。
- ラベルアンカー付きの Twilio 認証トークン。
- SendGrid API キー。
- Shopify トークン。
- ラベルアンカー付きの Supabase サービスロールキー。
- ラベルアンカー付きの Vercel トークン。
- npm トークン。
- GitLab パーソナルアクセストークン。
- Discord Webhook URL。
- ラベルアンカー付きの Cloudflare API トークン。
- ラベルアンカー付きの Notion インテグレーショントークン。
- Linear API キー。
- 認証情報を含むデータベース URL。
- JWT。
- Bearer トークン。
- 汎用的な API キーまたはシークレットキーの代入。
- 秘密鍵の PEM ブロックヘッダー。
- dotenvx の秘密鍵(
DOTENV_PRIVATE_KEY*の代入)。
生成された gitleaks 由来のルールは、プロバイダー固有の API キー、アクセストークン、クライアントシークレット、Webhook URL、クラウド認証情報、パッケージレジストリトークン、および関連するシークレット形式について、より広範なサービスのカバレッジを追加します。これらのルールは、キーワードプレフィルター、パス限定ルール、報告されるシークレット値のための secretGroup 抽出、エントロピーしきい値、ルール単位の許可リスト(allowlist)など、gitleaks の主要なルールセマンティクスを可能な範囲で維持しています。
生成された gitleaks ルールの ID は、既存の shk ルール ID と衝突しないよう secret.gitleaks.<upstream-id> の名前空間を使います。少数の上流ルールは意図的にスキップされています。generic-api-key と openai-api-key は調整済みの shk ルールと重複しており、その他のいくつかは Rust の regex のコンパイルサイズ上限を超えるか、内容の正規表現を持ちません。それ以外の重複するルールは維持されるため、1 つの値が調整済みルールと secret.gitleaks.* ルールの両方で報告されることがあります(たとえば medium の secret.jwt と high の secret.gitleaks.jwt)。gitleaks のライセンスとソースコミットについては THIRD_PARTY_LICENSES.md を参照してください。
これらはパターンベースの検出です。確定した認証情報として扱う前に、検出結果を確認してください。
フックのアクションガード
pre-hook モードでは、shk scan --hook-mode <tool> はテキストコンテンツをスキャンする前に、AI ツールのペイロードに危険なアクションが含まれていないかをチェックします。このガードはシークレットおよび PII 検出とは独立しており、ファイルパスやシェルコマンドといった操作の意図を確認します。
初期状態のガードは、機密ファイルの読み書き、.env のダンプコマンド、printenv、env、export -p、set | ... などの環境変数ダンプコマンド、シェルの -c による環境変数ダンプ、Python の os.environ、Node の process.env、Ruby の ENV、Perl の %ENV などの一般的なインタープリターによる環境変数の読み取り、破壊的な再帰削除、データベースへの直接的な変更コマンド、権限やシステムの変更、外部への転送コマンド、パッケージマネージャーの操作をブロックします。プロジェクトでは shk.toml の [action_guard] で、profile、allow、deny パターンなどを調整できます。strict プロファイルでは、bash -c、python -c、node -e のような不透明な実行は、詳細に解釈されるのではなくブロックされます。監査モードではブロックせずに検出結果の記録のみを行います。
一般的な zsh、bash、sh、fish、PowerShell PSReadLine の履歴ファイルは機密パスとして扱われます。 サポートされているネストしたシェルペイロードは再帰的に検査され、strict プロファイルではさらに、 内容自体は安全であっても不透明な実行を拒否します。
シェル履歴の保護はパスとコマンドに基づくものであり、完全なシェルインタープリターではありません。
カスタムの履歴保存先や、認識されないコマンドを経由した間接的な読み込みは、意図的な制限事項として
残っています。そうしたパスやワークフローをカバーする必要がある場合は、プロジェクト固有の
[action_guard].deny パターンを追加してください。
MCP 設定監査
shk mcp audit は、コンテンツスキャンとは別の検出モデルを使います。プロジェクトファイル内のテキストを
照合する代わりに、MCP クライアントの設定ファイルを解析し、各サーバーエントリがどのように宣言されているかを
評価します。この監査は静的であり、サーバーの起動、コマンドの解決、変数の展開、ネットワークリクエストを
一切行いません。
検出結果は種類 mcp を使い、設定ファイルの 1 行目 1 列目として報告されます。これは、検出結果の対象が
テキスト上の位置ではなくサーバーエントリだからです。
| ルール | 重要度 | 検出内容 |
|---|---|---|
mcp.npx_auto_install |
medium |
npx -y または npx --yes による自動パッケージインストール。 |
mcp.unpinned_package |
medium |
正確なバージョン指定のない npx、uvx、pipx run パッケージ。 |
mcp.shell_wrapper |
medium |
実際に実行されるコマンドを隠す、-c または /c を使うシェルラッパー。 |
mcp.local_unpinned_executable |
low |
整合性検証のない相対パスまたは非システムの実行ファイルパス。 |
mcp.broad_filesystem_scope |
high / medium |
/ またはユーザーのホームディレクトリを公開するファイルシステムサーバー。 |
mcp.http_no_tls |
high |
http:// を使うループバック以外のリモートエンドポイント。 |
mcp.secret_in_url |
high |
サーバー URL に含まれる機密性の高いクエリパラメータ名。 |
mcp.unknown_transport |
info |
コマンドも URL も宣言していないエントリ。 |
mcp.config_unreadable |
low |
読み取りまたは解析できないファイル(後述の読み取り上限で拒否されたエントリを含む)。 |
mcp.env_file_unreadable |
low |
存在はするが安全に読み取れない --env-file の対象(サイズ超過、または通常ファイルではない)。 |
設定された引数、プロセス変数、ヘッダー、URL の値はさらに組み込みのシークレットルールを通過するため、
サーバー定義内の平文の認証情報は、通常の secret.* ルール ID と種類 secret で報告され、メッセージは
サーバー、クライアント、フィールドを示すように書き換えられます。${VAR}、$VAR、${input:token} などの
参照は間接参照として認識され、平文の値としては扱われません。既存の [[allowlist]] エントリが適用されます。
サーバーの --env-file 引数で指定された平文ファイルは、設定自体には決して現れない認証情報の運び手であるため、
監査はその参照をたどり、同じシークレットおよび dotenv ルールでファイルの内容をスキャンします(同じ 1 MiB の
読み取り上限が適用されます)。このローカルファイルの読み取りは「監査は設定ファイルのみを読む」という原則の
唯一の例外であり、それでも監査はサーバーの起動、変数の展開、ネットワークへのアクセスを一切行いません。
相対パスは設定ファイルのディレクトリを基準に解決され、${VAR} 形式の参照はたどられず、解決後の対象は
選択されたプロジェクトまたはホームのスコープ内に留まる必要があります。存在しない対象はスキップされ、
スコープ外に出る、または安全に読み取れない既存のファイルのみが mcp.env_file_unreadable になります。
読み取りは解析前に制限されます。ファイルあたり 1 MiB、ファイルあたり 1,000 サーバーエントリまでで、解決後の
パスは選択されたプロジェクトまたはホームのスコープ内に留まる必要があります。スコープ外へ出るシンボリックリンク、
サイズ超過のファイル、過剰なサーバーマップ、通常ファイル以外、および(Unix では)ハードリンクされた設定ファイルは、
読み取られる代わりに mcp.config_unreadable として報告されます。読み取れないファイルが 1 つあっても監査は中断されません。
レポートには、プロセス変数の値、ヘッダーの値、生の一致内容は含まれません。監査対象のファイルの場所と
終了コードについては shk mcp audit を参照してください。
PII のカバレッジ
汎用の PII ルールは pii = true のときに実行されます。
- メールアドレス。
- Luhn 検証済みのクレジットカード番号。
- IPv4 アドレス。
- IPv6 アドレス。
- 平文のメールアドレスまたは電話番号と同じ行にある仮名化トークン(
email_…、phone_…、name_…)(pii.shk_plaintext_map、high)。マッピングの漏えいを示します。
英語の PII ルールは、pii = true かつ pii_languages に en が含まれるときに実行されます。
- 電話番号。
- ラベルアンカー付きの米国社会保障番号(SSN)。
- ラベルアンカー付きの ZIP コードまたは郵便番号。
- ラベルアンカー付きの EIN。
- ラベルアンカー付きのパスポート番号。
- ラベルアンカー付きの住所。
- ラベルアンカー付きの個人名。
日本語の PII ルールは、pii = true かつ pii_languages に ja が含まれるときに実行されます。
- 電話番号。
- ラベルアンカー付き、または郵便記号付きの郵便番号。
- ラベルアンカー付きのパスポート番号。
- ラベルアンカー付きのマイナンバー。
- ラベルアンカー付きの法人番号。
- ラベルアンカー付きの運転免許証番号。
- ラベルアンカー付きの銀行口座のパターン。
- ラベルアンカー付きの健康保険証のパターン。
- ラベルアンカー付きの個人名。
- 住所(都道府県、市区町村、番地。
lowで報告されます)。
電話番号、メールアドレス、クレジットカード番号、IP アドレス、日本語の住所を除き、PII ルールは誤検知を減らすためにラベルアンカー付きになっています。
バイナリファイルと大きなファイル
デフォルトでは、スキャナーの走査は scan.max_file_size_bytes より大きいファイルと、バイナリと思われるファイルをスキップします。バイナリ判定は、先頭 scan.binary_detection_bytes バイトに NUL バイトが含まれるかをチェックします。
人間向けの出力では、--verbose を使わない限り情報レベルのスキップ検出結果は非表示になります。JSON 出力では、スキップ検出結果が含まれます。
バイナリと思われるファイルもスキャンするには、--include-binary または scan.include_binary = true を使ってオプトインしてください。
ドキュメントのテキスト抽出
バイナリのスキップに先立ち、shk scan はサポートされているドキュメント形式に対してテキスト抽出を試みます。
.docx:word/document.xmlをスキャンします。.xlsx: 共有文字列とワークシート XML のテキスト(インライン文字列、セルの値、数式)をスキャンします。.pptx: スライド、ノートスライド、コメントのテキストをスキャンします。.pdf: 埋め込まれたテキストレイヤーをスキャンします。
Office の検出結果は、report.docx:word/document.xml や workbook.xlsx:xl/sharedStrings.xml のような内部エントリラベルを使います。PDF の検出結果は PDF ファイルのパスそのものを使います。パスベースの許可リスト(allowlist)はこれらのラベルに一致させる必要があります。
抽出器は、スキャン前に論理的なドキュメントテキストグループ内のテキストを結合することで、Office で一般的なリッチテキストの分割を処理します。PDF サポートは OCR を行いません。画像のみの PDF は、テキストを抽出できない場合に scan.document_text_empty を報告します。
Office の ZIP コンテナは、エントリ数の上限、エントリごとの展開上限、および scan.max_file_size_bytes から導出される累積展開サイズの上限のもとで処理されます。これらの上限を超えるドキュメントは、メモリ上で完全に展開される代わりに scan.file_read_error として報告されます。
JSON 出力
shk scan . --json
shk scan . --json --with-value-hashレポートの例:
{
"version": 1,
"scanned_paths": ["src/app.ts"],
"findings": [
{
"rule_id": "secret.openai_api_key",
"severity": "high",
"kind": "secret",
"file": "src/app.ts",
"line": 12,
"column": 18,
"message": "Possible OpenAI API key detected",
"redacted_value": "[REDACTED]",
"confidence": 0.9
}
],
"summary": {
"total": 1,
"by_severity": {
"high": 1
}
},
"exit_threshold": "high",
"suppressed": 0,
"deduplicated": 0,
"color_mode": "never"
}JSON スキャンには、コンテキスト行が利用できる場合、秘匿化された周辺コンテキストが含まれます。空のコンテキストフィールドはシリアライズされたレポートから省略され、shk.toml が解決された場合は policy_path フィールドが追加されます。1 つのスキャン対象ファイル内で同じルールと値が繰り返し検出された場合は 1 回だけ出力され、deduplicated にカウントされます。
--with-value-hash を渡すと、各コンテンツ検出結果に value_hash も含まれます。これは HMAC-SHA256(raw_value, rule_id) を sha256-hmac:<hex> の形式にしたものです。一致した生の値を出力することなく、値固有の [[allowlist]] エントリや shk allowlist suggest --value-hash をサポートします。
値ハッシュ(value hash)は決定的で、公開されているルール ID をキーとします。生の値そのものではありませんが、一般的なメールアドレス、名前、電話番号などのエントロピーの低い値は辞書攻撃で復元できる可能性があります。特に SARIF や CI ログをサードパーティのシステムにエクスポートする場合は、値ハッシュを含むレポートを機密性の高い成果物として扱ってください。
マスキングモデル
shk mask は入力をスキャンし、ポリシーに従って一致した行または値を秘匿化します。
min_severity = "medium"はデフォルトでmedium、high、criticalの検出結果を秘匿化します。マスクのしきい値を下げたり上げたりするには、--min-severityまたは[mask].min_severityを使ってください。redaction = "match"は一致した値のみを[REDACTED]に置き換えます。redaction = "full"は、1 つ以上の検出結果を含む各行を[REDACTED_LINE]に置き換えます。redaction = "partial"は、preserve_prefixとpreserve_suffixの文字数を保持しつつ、一致した値を[REDACTED]マーカーに置き換えます。
バイナリまたは UTF-8 以外の入力は shk mask ではスキャンされません。人間向けの出力ではそのまま通過させ、JSON 出力では mask.binary_passthrough を報告し、マスク済みコンテンツとして [BINARY_PASSTHROUGH] を使います。
Office ドキュメントのマスキングは --output 付きで .docx、.xlsx、.pptx をサポートします。新しいドキュメントを書き出し、元のファイルは変更しません。PDF のマスキングはサポートされていません。
Office のマスキングはテキスト以外の ZIP エントリをストリーミングし、同じ制限付き展開ポリシーを適用し、隣接する一時ファイルに書き込みます。要求された出力先は、マスク済みアーカイブ全体が確定して同期された後にのみ置き換えられます。
監査ログ
shk scan --hook-mode <tool> --audit は .shk/audit.log に JSON 行を書き込みます。監査エントリには、ツール名、フックのフェーズ、表示パス、検出結果数、抑制数、重複排除数、最大重要度などのメタデータが含まれます。一致した生の値は含まれません。
アクティブな監査ログは 8 MiB を上限とし、audit.log.1 から audit.log.3 へローテーションされます。shk audit は上限付きのアーカイブを古いものから新しいものへ順に読み込み、1 行ずつ解析します。
shk scan --hook-mode <tool> --log-blocked は pre-hook のブロック動作を維持しつつ、ブロックされた pre-hook イベントとユーザープロンプトイベントについて、メタデータのみの event = "blocked" エントリを書き込みます。--post を付けた場合は非ブロッキングのままで、post-hook スキャンについて event = "audit" エントリを書き込みます。検出結果しきい値によるブロックエントリには、アクティブなしきい値以上の検出結果についてルール ID、検出結果の種類、件数、最大重要度が含まれます。アクションガードによるブロックエントリにはアクションのカテゴリのみが含まれ、コマンドテキスト、ファイルパス、プロンプト本文、ガードの理由は含まれません。
.shk/audit.log をプレビューするには shk audit を使います。
shk audit
shk audit --reason finding-threshold
shk audit --reason action-guard --no-paths
shk audit --since 7d --tool cursor --jsonshk secrets push --audit も .shk/audit.log にメタデータのみの JSON 行を書き込みます。シークレット push の監査エントリには、プロバイダー、モード、ソースラベル、バイト数、ペイロードの SHA-256 ハッシュ、ターゲットラベル、キー数、操作、ステータスなどのフィールドが含まれます。生の dotenv 値やキーごとのシークレットペイロードは含まれません。