auditing-supply-chain について
依存関係、CI/CD パイプライン、ビルド成果物を評価する、開発・セキュリティチーム向けのサプライチェーン監査スキルです。既知の脆弱性と悪意あるパッケージの両方を扱い、固定、認証情報、SBOM、来歴を確認します。
主な機能
- 01直接・間接依存関係の確認
- 02悪意あるパッケージとインストールスクリプトのリスク検出
- 03CI/CD の権限とワークフローの監査
- 04シークレットの検査とビルド設定の確認
- 05SBOM の生成と再スキャン
- 06成果物の署名と来歴の評価
活用シーン
- 01GitHub Actions とリリース自動化の監査
- 02不審な依存関係や上流侵害の調査
- 03到達可能性と露出度に基づく脆弱性の優先順位付け
- 04ビルド成果物の署名と来歴の検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trilwu/ secskills --skill auditing-supply-chainインストール後、Claude Code で/auditing-supply-chainを入力して読み込まれたか確認します。
詳細
サプライチェーンの監査
ビルドパイプラインでは、アプリケーションよりも多くの信頼できないコードが実行されます。アクションのピン留め漏れ、postinstall スクリプト、書き込み可能なトークンを持つワークフローが一つあるだけで、見知らぬ人のコミットから本番環境の成果物や署名キーに至る経路が生まれます。
使用する場面
- アプリケーションにおけるサードパーティ依存関係のリスク評価
- CI/CD パイプライン、GitHub Actions、リリース自動化のレビュー
- 不審または悪意のあるパッケージの調査
- SBOM の作成またはレビュー
- ビルド来歴と成果物の署名の強化
- 上流での侵害の公表を受けた対応
使用しない場面
- 自社コードの脆弱性 —
auditing-code-for-vulnerabilitiesを使用してください - モデルとデータセットの来歴 —
securing-ai-systemsを使用してください - クラウドインフラ全般のセキュリティ態勢 — 攻撃側の評価には
exploiting-cloud-platformsを使用してください - 進行中の侵害 —
responding-to-incidentsを使用してください - 多数の依存関係 CVE のうち、どれを最初に修正するかの優先順位付け —
managing-vulnerabilitiesを使用してください。ここでの到達可能性分析は、その順位付けに役立ちます
2 つの異なるリスク
これらは分けて考えてください。必要な対応はそれぞれ異なります。
| 既知の脆弱性がある依存関係 | 悪意のある依存関係 | |
|---|---|---|
| 検出 | CVE データベース、npm audit、osv-scanner | 振る舞いのレビュー、インストールスクリプト、公開者の不審な点 |
| 特徴 | 目立ち、優れたツールがある | 目立たず、通常スキャナーでは見逃される |
| 対応 | パッチを適用するか、リスクを正当化する | インシデントとして対応する — ビルドホスト上の認証情報は漏えいしたと想定する |
| 時間的猶予 | 数日から数週間 | 数時間 |
ほとんどのプログラムは前者には対応していますが、後者には気づいていません。後者にも明示的に注意を払ってください。
依存関係のレビュー
# Known vulnerabilities, ecosystem-agnostic
osv-scanner --lockfile=package-lock.json --lockfile=go.sum --lockfile=Cargo.lock
trivy fs --scanners vuln,secret,misconfig .
grype dir:.
# Ecosystem-native
npm audit --omit=dev && npm ls --all --depth=99 | wc -l # count transitives
pip-audit -r requirements.txt
cargo audit
govulncheck ./... # reachability-aware: only reports vulns you actually call
mvn dependency-check:check
govulncheck のような到達可能性分析が重要です。実行されることのないコードパスにある脆弱性は、リスクではなくパッチ適用のタスクです。CVSS だけでなく、到達可能性と露出度に基づいて優先順位を付けてください。
実際の攻撃対象領域は推移的依存関係です。 直接依存関係は選定され、レビューされていますが、推移的依存関係はそのまま引き継がれます。数を把握し、暗黙のうちに信頼しているメンテナーを把握してください。
悪意のあるパッケージの検出
悪意を示す度合いが強い順に並べた、おおよそのトリアージシグナルです。
| シグナル | 確認方法 |
|---|---|
| インストール時のスクリプト実行 | package.json の postinstall/preinstall、ネットワーク通信や exec 呼び出しを含む setup.py、build.rs |
| 難読化または圧縮されたソースコードが、圧縮されていないパッケージに含まれる | リポジトリではなく、公開済みの tarball を確認します。両者は異なる場合があります |
| import/require 時のネットワーク呼び出し | モジュールのトップレベルにある HTTP/DNS 呼び出しを静的 grep で確認します |
| 環境変数や認証情報へのアクセス | ~/.aws、.npmrc、.git-credentials の読み取り、process.env のダンプ |
| 新しいメンテナー、または新規アカウントから公開されたバージョン | レジストリのメタデータ、公開履歴 |
| 人気パッケージに似た名前 | 人気パッケージの上位リストを対象にレーベンシュタイン距離を確認します |
| 公開済み成果物とリポジトリのソースが一致しない | tarball とタグ付きコミットを比較します |
| 対応するコミットがないバージョンの大幅な更新 | レジストリと VCS の履歴を比較します |
# Review what actually ships, not what the repo shows
npm pack <pkg> && tar -xzf <pkg>.tgz && rg -n 'child_process|eval\(|Buffer\.from\(.*base64|https?://' package/
pip download --no-deps --no-binary :all: <pkg> && tar -xzf <pkg>.tar.gz
rg -n 'os\.system|subprocess|urllib|requests|__import__|exec\(' <pkg>/setup.py
# Block install scripts by default in CI
npm ci --ignore-scripts
pip install --require-hashes -r requirements.txt
依存関係の混同:内部パッケージ名が公開レジストリにも登録(または予約)されておらず、リゾルバーが公開レジストリにアクセスできる場合、攻撃者はより高いバージョンを公開し、依存関係の解決で優先させることができます。
# Enumerate internal-looking names and check public availability
rg -o '"@?[a-z0-9-]+/[a-z0-9-]+"' package.json | sort -u
# Fix: scoped registries with strict scope→registry mapping, and
# `.npmrc` / `pip.conf` that never falls back to the public index for
# internal scopes
ロックファイルとピン留め
- ロックファイルはコミットし、PR でレビューする必要があります。また、CI では依存関係を新たに解決するのではなく、ロックファイルからインストールする必要があります(
npm ci、pip install --require-hashes、cargo --locked、go mod verify)。 - 変更内容と関係のないパッケージに影響する PR のロックファイル差分は、ノイズではなくレビュー対象の兆候です。
- エコシステムが対応している場合は、整合性ハッシュでピン留めしてください。バージョンのピン留めだけでは、再公開が可能なレジストリで同じバージョンが再公開された場合に保護できません。
CI/CD パイプラインのセキュリティ
最も影響の大きい問題が見つかるのは、たいていここです。
GitHub Actions
# Unpinned third-party actions — anyone who controls the tag controls your CI
rg -n 'uses:\s+(?!actions/)[^@]+@(?!v?[0-9a-f]{40})' .github/workflows/
# The dangerous trigger: pull_request_target runs with repo secrets and
# write-capable tokens, in the base repo context
rg -n 'pull_request_target|workflow_run' -A15 .github/workflows/
# Script injection: untrusted event data interpolated directly into a shell
rg -n '\$\{\{\s*github\.event\.(issue|pull_request|comment|head_commit)' .github/workflows/
すべてのリポジトリで確認すべき所見は3つです。
pull_request_targetと PR の head のチェックアウト。 これにより、見知らぬ人のコードがシークレットを使って実行されます。ワークフローで、チェックアウトしたツリーからビルドやテストの手順も実行している場合は、重大な所見です。- 信頼できない値の
run:への埋め込み。 シェルブロック内の${{ github.event.issue.title }}は、外部から到達可能なコマンドインジェクションの入口になります。代わりにenv:変数を経由させ、引用符で囲んでください。 - 過剰に広い
permissions。 デフォルトのGITHUB_TOKENの権限範囲はcontents: readとし、必要な箇所でのみジョブごとに権限を引き上げてください。permissions: write-allがないか、またpermissions:ブロックが存在しないかを確認してください。
次の点も確認してください。公開リポジトリでのセルフホストランナーの使用(永続的な侵害や、ジョブ間の分離の欠如)、フォークから起動されたワークフローで利用可能なシークレット、ブランチ間でのキャッシュポイズニング、認証情報を含むビルドディレクトリのアーティファクトへのアップロード。
一般的なパイプライン
# Secrets in history, not just in the tree
gitleaks detect --source . --redact
trufflehog git file://. --only-verified
# IaC and container config
trivy config . && checkov -d .
hadolint Dockerfile
確認事項:デプロイを起動できるのは誰か、デプロイ用の認証情報が環境ごとにスコープ設定されているか、ビルドを再現できるか、アーティファクトに署名されているか、リリースブランチに誰でも直接プッシュできるか。
SBOM と Provenance
# Generate — from the build, not from the source tree, so it reflects reality
syft dir:. -o cyclonedx-json=sbom.json
cdxgen -o sbom.json
# Consume — an SBOM is only useful if you scan it on a schedule
grype sbom:sbom.json
osv-scanner scan source -L sbom.json # v2 takes SBOMs via -L; --sbom is gone
コンプライアンス上のチェック項目を満たすために一度だけ作成された SBOM に、セキュリティ上の価値はありません。価値があるのは、新たな脆弱性が見つかったときに既存の SBOM を再スキャンし、「影響を受けているか」という問いに数日ではなく数分で答えられることです。
Provenance(SLSA の枠組み):特定のアーティファクトが、どのソースコミットから、どのビルダー上で、どの依存関係を使って生成されたかを証明できますか。アーティファクトに署名し(cosign)、アテステーションを記録し、デプロイ時に署名を検証してください。検証されない署名は飾りにすぎません。
cosign sign --key <key> <image>
cosign verify --key <pub> <image>
cosign verify-attestation --type slsaprovenance <image>
上流の侵害への対応
1. Determine exposure: did any build pull the affected version? Check
lockfiles across branches AND build logs — the lockfile shows intent, the
build log shows what was actually installed.
2. Assume credential compromise on any host that ran the package's install
scripts. Rotate: registry tokens, cloud keys, signing keys, SSH keys.
3. Preserve build logs and runner images before they roll off.
4. Check outbound network from build hosts for the exfil window.
5. Pin and rebuild; verify the rebuilt artifact differs only as expected.
6. Only then publish an advisory.
そのパッケージが「CI でしか実行されなかった」からといって、ローテーションが不要になるわけではありません。CI には本番環境の認証情報が保存されています。
退けるべき合理化
- 「開発用の依存関係です。」 開発用の依存関係は、完全な認証情報を持つ開発者のノートパソコンやビルドサーバーで実行されます。本番環境よりも狙われやすい対象です。
- 「週に1,000万回ダウンロードされています。安全に違いありません。」 人気があるからこそ標的になります。最大規模のインシデントのいくつかは、トップ100に入るパッケージで発生しました。
- 「スキャナーに CVE は表示されていません。」 スキャナーが検出するのは既知の脆弱性です。最初の公開時点から悪意のあるパッケージには、CVE が存在しません。
- 「後で固定します。」 固定されていないアクションとイメージは、継続的なリスクです。固定には数分しかかかりません。
- 「このワークフローは PR でしか起動されません。」 PR での
pull_request_targetこそ、まさに危険なケースです。 - 「内部用のパッケージ名なので、誰にも分かりません。」 パッケージ名は、エラーメッセージ、ソースマップ、ジョブログ、公開フォークを通じて漏えいします。
- 「SBOM を生成しました。」 生成することと監視することは別です。
成果物
- 直接依存と間接依存の数、およびメンテナーの集中度を含む依存関係の一覧
- 到達可能性と露出状況に基づいて優先順位付けした既知の脆弱性の所見
- 確認したシグナルを含む、悪意のあるパッケージのトリアージ結果
pull_request_target、未固定のアクション、トークンの権限範囲、インジェクションのシンクについて、それぞれ存在するかどうかを明記したパイプラインの所見- シークレットの露出状況(ツリーと履歴)およびローテーションの状況
- Provenance の成熟度:固定、署名、アテステーション、デプロイ時の検証
- 「パッチ」と「アーキテクチャ」に分けて優先順位付けした修正対応
ATT&CK のカバレッジ
secskills-core/ttp-index.json から生成されます。このファイルを編集してから、
python3 scripts/sync_attack.py --write を実行してください。レポートで引用する前に、
現在の ATT&CK リリースと照らし合わせて ID を再確認してください。
初期アクセス(TA0001)
防御回避(TA0005)
- T1553 信頼制御の回避 —
analyzing-malwareも参照してください
認証情報アクセス(TA0006)
- T1552 安全でない認証情報 —
escalating-linux-privileges、exploiting-cloud-platforms、abusing-ci-cd-oidcも参照してください
これらのいずれかに関する検出コンテンツ:engineering-detections。プロアクティブな検索:hunting-threats。侵害後の対応:responding-to-incidents。
外部ソースを読む
公開アドバイザリ、仕様書、ベンダーレポートを Markdown として取得します:
curl -sL "https://defuddle.md/<url>" # scheme in the path is optional
これによりページの定型部分が取り除かれ、文章中心のページではトークン数が約 78% 減ります。また、要約ではなく全文が返されるため、grep で検索でき、見つからなかった場合も信頼できます。
この用途に適さないケースが 3 つあります。JSON と API のレスポンスは、構造化データが読みやすさ抽出によって損なわれるため、生のまま取得してください。認証が必要なページや JavaScript でレンダリングされるページは、匿名で取得されるため、直接取得してください。また、攻撃者のインフラストラクチャ(フィッシングリンク、C2、マルウェアホスティング)、クライアント所有のホスト、エンゲージメント URL は、決してこの経由でアクセスしないでください。リクエストがマシンから第三者に送信され、稼働中の攻撃者インフラストラクチャの場合は、オペレーターに気付かれる可能性もあります。
サイトによっては抽出をブロックし、ページではなくエラーの内容を返すことがあります。たとえば、freedesktop.org からは {"error":"Failed to fetch: 418 I'm a teapot"} が返されます。これは取得が拒否されたことを示すものであり、ソースがその内容の不存在を示しているわけではありません。そこから結論を導く前に、URL を直接再取得してください。
参考資料
auditing-code-for-vulnerabilities— 自社コードのレビューsecuring-ai-systems— モデルとデータセットのサプライチェーンresponding-to-incidents— 上流での侵害が確認された場合の対応- SLSA フレームワーク、OpenSSF Scorecard、CycloneDX/SPDX、Sigstore/cosign
osv-scanner、trivy、grype、syft、gitleaks、zizmor(Actions の監査)