dependency-audit について
新しいサードパーティーパッケージやメジャーバージョン更新を評価するための、サプライチェーン監査手順です。身元、脆弱性、動作を確認し、Docker の隔離環境で依存ツリーを解決して調査することを求めます。
主な機能
- 01パッケージの身元・バージョン・ライセンス確認
- 02既知の脆弱性と依存リスクの確認
- 03パッケージとインストールスクリプトの動作調査
- 04Docker の隔離環境で依存ツリーを検査
- 05採用または却下の判断基準
活用シーン
- 01追加前のパッケージ審査
- 02メジャー更新前のリスク評価
- 03不審なスクリプトや推移的依存の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ petr-korobeinikov/ skills --skill dependency-auditインストール後、Claude Code で/dependency-auditを入力して読み込まれたか確認します。
同梱ファイルreferences/ecosystems.mdreferences/services.md
詳細
dependency-audit
サードパーティ製の依存関係を追加することは、利便性の問題ではなく、サプライチェーンに関わる判断です。 取り込んだすべてのパッケージは、マシン上および CI でコードを実行し、 推移的依存関係も一緒に持ち込み、 あなたの信頼を受け継ぎます。 そのため、以下の監査を通過するまで、 どの言語でも、 直接依存か推移的依存かを問わず、 プロジェクトに追加してはいけません。
npm install / go get / pip install / cargo add を
気軽に実行したり、
マニフェストを直接編集したりすることは、
パッケージがこのパイプラインを通過するまで禁止です。
「今回だけ」はありません。
未審査のパッケージを手元で手早く試すだけでも、
同じようにホスト上でインストールスクリプトが実行されます。
どうしても試す必要がある場合は、まずサンドボックス(Stage 4)で試してください。
迷った場合は、オペレーターに確認してください
不明瞭な点がある場合、 ツールが見つからない場合、 または兆候が不注意と悪意の境界にある場合は、 推測で採用せずに作業を止め、オペレーターに確認してください。 不適切な依存関係を削除するには大きなコストがかかり、 気づくまでにコードがすでに実行されている可能性もあります。 ロックファイルが侵害されてからではなく、質問を一つするほうが安上がりです。 これは以下のすべての手順に当てはまります。
実行するタイミング(ゲート)
新しいサードパーティ製依存関係を追加する場合、 または既存の依存関係をメジャーバージョンをまたいで更新する場合は、 この手順を実行してください (メジャーバージョンの更新は、新たな推移的依存関係を持ち込みます)。 具体的には、以下を行う前に実行します。
npm install <pkg>/yarn add/pnpm add/bun addgo get <module>pip install <pkg>/poetry add/uv add/requirements.txtまたはpyproject.tomlへの行の追加cargo add <crate>implementation(...)/<dependency>を追加するためのbuild.gradle/build.gradle.kts/pom.xmlの編集gem install/bundle add、composer require、dotnet add packageなど、各エコシステムにおける同等の操作すべて
これは本番環境、開発、テスト、ビルドツール/プラグインの依存関係すべてに当てはまります。
開発用依存関係の postinstall スクリプトも、
本番用のものと同じように、マシン上および CI で実行されます。
言語の標準ライブラリや、 信頼できる独自のレジストリにあるファーストパーティ製/内部パッケージには適用されません。 監査済みの依存関係をパッチバージョンに再固定する場合は、完全なパイプラインは不要です。 メジャーバージョンの更新には必要です。
監査パイプライン
5 つのステージがあります。 Stage 0~3 は読み取り専用の情報収集で、 対象が確定すれば並行して実行できます。 Stage 4 だけがパッケージのコードを実行し、 必ず隔離環境で実行します。 どのステージでも採用を妨げる検出事項があった場合、 説明がつくかパッケージが却下されるまで、採用を中止してください。
エコシステムごとのコマンドは
references/ecosystems.md にあります。
サービスの構文と URL(osv-scanner、OSV.dev、GitHub Advisories、
Socket、Snyk)は
references/services.md にあります。
ステージ 0 — 識別と衛生管理
スキャンの前に、監査対象を正確に特定してください。 サプライチェーン攻撃は、 「使うつもりだったパッケージ」と「入力したパッケージ」の間の隙を突きます。
- 採用する予定の正確なパッケージ名、レジストリ/名前空間、 および特定のバージョン。 タイポスクワッティング、 依存関係の混同 (公開パッケージが内部名を装うこと)、 および見た目が似ているスコープ付きの名前に注意してください。
- 正規のプロジェクトであることを確認してください。 リポジトリへのリンク、ホームページ、ダウンロード数、リリース頻度、 最終公開日、メンテナー、 およびそのバージョンが取り下げられたり非推奨になったりしていないことを確認します。
- ライセンス — このプロジェクトと互換性がありますか? 脆弱性がないパッケージでも、ライセンスが適切でなければ採用できません。
ここでの危険信号だけで監査を中止する場合もあります。 履歴のない新しいパッケージが人気パッケージになりすましている、 メンテナーが突然変わった、 数時間前に公開されたバージョンで異常な活動が急増している、などです。
何もインストールせずに、これらの情報を取得してください。
deps.dev(https://deps.dev/<ecosystem>/<name>)は、
npm/PyPI/Go/Maven/Cargo/NuGet のバージョン、ライセンス、アドバイザリ、メンテナーデータを網羅しています。
また、各レジストリにはそれぞれメタデータを取得するコマンド
(npm view、PyPI JSON API、go list -m -versions など)があります。
references/services.md を参照してください。
ステージ 1 — 既知の脆弱性(CVE/OSV)
パッケージ本体とその推移的依存関係に、 既知の公開済み脆弱性が含まれていないか確認してください。 候補はまだプロジェクトに追加されていないため、 スキャン対象となるホスト側のロックファイルはありません。 また、ホスト上で依存関係を解決してロックファイルを作成してはいけません。 次の 2 つの方法なら、その制約を守れます。
- 名前とバージョンを指定して照会する — ロックファイルもインストールも不要です。
OSV.dev の
/v1/query、 GitHub Advisory Database、 または各エコシステムの単一パッケージ検査 (例:snyk test <pkg>@<ver>、npm のみ)を使います。 - ステージ 4 のサンドボックス内で、解決済みの依存関係ツリー全体をスキャンする。
実際のロックファイルが存在し、コンテナ外に出ることはありません。
osv-scannerでスキャンするか、 エコシステム標準の監査ツール (npm audit、pip-audit、govulncheck(Go、到達可能性を考慮)、cargo audit、OWASP dependency-check(JVM))を、 依存関係を解決したものと同じ使い捨てコンテナ内で実行します。
どの照会方法を使う場合も、
ツールがエコシステムのスラッグを受け付けることを確認してください。
OSV は不正なスラッグを HTTP 400 Invalid ecosystem で拒否します。
もっともらしく見えて誤ったスラッグを指定すると、該当なしの結果が返されます。
どちらも安全の証拠ではなく、照会対象を誤ったことを示しているだけです。
修正されておらず、到達可能で、修正版も存在しない既知の脆弱性は、 採用を阻む重大な指摘事項です。
ステージ 2 — サプライチェーン上の挙動(Socket)
CVE データベースが把握しているのは、
すでに誰かが報告した脆弱性だけです。
Socket はもう一方の側面、つまりパッケージが何をするかを検出します。
インストール/postinstall スクリプト、
ネットワーク/ファイルシステム/環境変数へのアクセス、
シェルへのアクセス、
難読化されたコードまたは最小化されたコードしかないこと、
ネイティブコード、
タイポスクワッティング、
およびメンテナーや機能の突然の変更など、
CVE が報告される前に悪意のあるパッケージや侵害されたパッケージを見分ける手掛かりです。
高リスクのアラートは、説明がつくまで採用を阻む指摘事項として扱ってください。 ネットワークにアクセスするインストールスクリプト、 難読化されたペイロード、 パッケージの説明された目的と合わない機能 (文字列ヘルパーがソケットを開くなど)が該当します。
ステージ 3 — 別の見解を確認する(Snyk)
Snyk の脆弱性データベースとパッケージの健全性表示は、
ステージ 1 の強力な独立した照合手段です。
重大度、CVSS、修正方法/アップグレード方法を含む既知の CVE、
ライセンス上の問題、
および健全性スコア
(セキュリティ/人気度/メンテナンス状況/コミュニティ)を確認できます。
snyk test(無料アカウントが必要)を使うか、
security.snyk.io の公開パッケージページを確認してください(ログイン不要)。
ステージ 4 — 隔離インストールと依存関係の調査
ここでゲートが効力を発揮し、 厳守すべき要件が適用されます。 未審査のパッケージをホスト上で解決またはインストールしてはいけません。 ホストのファイルシステムを一切マウントせず、 使い捨ての Docker コンテナー内で実行してください。 そうすれば、任意のコードを実行するインストールスクリプトが マシン、 SSH キー、 環境、 リポジトリに触れることはできません。
コンテナーは 2 つの役割を果たします。
- パッケージが引き込む完全な推移的依存関係ツリーを列挙します。 これが実際の影響範囲です。 ステージ 0~3 では最上位だけをサンプリングしていました。 2 行のパッケージが 100 個もの推移的依存関係を引き込むことがあります。 その一つひとつに、独自のサプライチェーンリスクがあります。
- インストール時の挙動を安全に観察します。 まずインストールスクリプトを実行せずに調査し、 必要な場合に限り、スクリプトを実行しつつネットワークを隔離して、 外部への接続を試みる動きを監視します。
手順は次のセクションにあります。
Docker サンドボックス
強化された基本コマンドです。
イメージとインストールコマンドはエコシステムに合わせて差し替えてください
(references/ecosystems.md)。
docker run --rm \
--network bridge \
--cap-drop ALL \
--security-opt no-new-privileges \
--pids-limit 512 \
--memory 1g \
<ecosystem-image> sh -c '<install-and-inspect>'
各フラグにはそれぞれ理由があります。
-v/--mountは指定しません。 最も重要な性質です。 ホストのファイルシステムは決して渡されません (「ホストのディスクをマウントしない」)。 コンテナーは一時レイヤー内で好きなことができますが、 ホストには一切影響しません。--rm— 終了時にコンテナーと書き込み可能レイヤーが破棄されます。 何も残りません。--network bridge— 調査(パス A)ではツリーを解決するためにレジストリへの接続が必要なので、 ネットワークに接続したままにします。 重要な隔離は、外部への接続を許可せずにインストールスクリプトを実行するパス B で行います。 ネットワーク接続中にダウンロードし、 実行前にネットワークを切断します。--cap-drop ALL— Linux のすべてのケイパビリティを削除します。 インストールにこれらは不要です。--security-opt no-new-privileges— setuid による権限昇格を阻止します。--pids-limit/--memory— 悪意のあるインストールスクリプトによるフォーク爆弾やメモリ爆弾の影響を制限します。- 追加の強化策(任意):
--read-onlyを指定し、ツールチェーンが書き込むすべてのパスに--tmpfsを設定します。/tmpだけでなく、キャッシュや作業ディレクトリ (npm の~/.npm、pip の~/.cache、インストール先)も対象です。 または、npm_config_cache/PIP_CACHE_DIRでそれらの保存先を変更します。 さらに、イメージが対応している場合は root 以外の--userを指定します。
2 パス方式:
パス A — 調査(エコシステムが対応している場合、コードを実行しません)。
インストールスクリプトを実行せずに、ツリーを解決して読み取ります。
ダウンロードには --network bridge が必要です。
それ以外のフラグはすべて制限したままにします。
# npm — --ignore-scripts skips pre/post-install hooks entirely
docker run --rm --network bridge --cap-drop ALL \
--security-opt no-new-privileges --pids-limit 512 --memory 1g \
node:lts-alpine sh -c \
'cd "$(mktemp -d)" && npm init -y >/dev/null \
&& npm install --ignore-scripts --no-audit --no-fund <pkg> \
&& npm ls --all'
ツリーを確認します。
推移的依存関係はいくつあるか、
予期しないものはないか、
リスクがあると分かっているものはないか、
深い依存関係チェーンを単一のメンテナーが管理していないかを調べます。
(Go と Cargo は依存関係の解決時にコードを実行しません。
pip にはすっきり使える --ignore-scripts がないため、ホイールを優先してください。エコシステムごとの注意事項を参照してください。)
パス B — 起爆(インストールスクリプトの動作を確認する必要がある場合に限ります)。
実行中に --network を変更することはできず、
--rm を指定するとダウンロードしたファイルが破棄されます。
そのため、コンテナーを デタッチモード で実行し、
スクリプトを無効にしたまま成果物を取得し、
ネットワークを切断してから、スクリプトをオフラインで実行します。
cid=$(docker run -d --network bridge --cap-drop ALL \
--security-opt no-new-privileges --pids-limit 512 --memory 1g \
node:lts-alpine sleep 600)
docker exec "$cid" sh -c 'cd /tmp && npm init -y >/dev/null \
&& npm install --ignore-scripts --no-audit --no-fund <pkg>' # fetch only
docker network disconnect bridge "$cid" # cut egress
docker exec "$cid" sh -c \
'wget -T3 -qO- https://registry.npmjs.org >/dev/null 2>&1 \
&& echo REACHABLE || echo "egress blocked"' # confirm
docker exec "$cid" sh -c 'cd /tmp \
&& npm rebuild --foreground-scripts' # run hooks, offline
docker rm -f "$cid"
ネットワークに接続できずに失敗した postinstall や、
環境変数を読み取ったり SSH キーを探したりする動作から、
そのスクリプトが何をしようとしていたのかが正確に分かります。
しかも、接触できるホストはありません。
npm rebuild を単独で実行するのには理由があります。
解決済みツリー全体(推移的依存関係も含む)のインストールフックを実行するためです。
マルウェアが潜んでいるのは、まさにそこです。
npm rebuild <pkg> では最上位のパッケージだけが起動し、それらを見逃します。
このコマンドは、エコシステムに相当するコマンド
(インストールフックを実行する手順)に置き換えてください。
採用するか、見送るか
以下をすべて満たす場合にのみ採用します。
- 修正パッチが未適用で、到達可能な既知の脆弱性がないこと(ステージ 1、3)。
- 説明のつかない高リスクな Socket の動作がないこと(ステージ 2)。
- 推移的依存関係による影響範囲を理解しており、許容できること(ステージ 4)。
- ライセンスに互換性があること。
- パッケージが実在し、保守されており、正しく特定されていること(ステージ 0)。
以下のいずれかに該当する場合は、見送るか、上位判断を仰いでください。
- 修正バージョンが存在しない、悪用可能な既知の CVE がある。
- 明確で正当な理由がないのに、インストールスクリプトがネットワークに接続したり、シークレットや環境変数を読み取ったりする。
- 難読化されたペイロード、または最小化されたソースしかないペイロード。
- タイポスクワッティング/依存関係の混乱/なりすましを示す兆候がある。
- 機能が不一致である (ソケットを開く「left-pad」など)。
- 保守されておらず、かつ独自のリスクの高い推移的依存関係チェーンを抱えている。
- ライセンスに互換性がない。
- 推移的依存関係による影響範囲が、得られる価値に対して著しく大きい (日付ヘルパーが 200 個のパッケージを取り込むなど)。
判断が微妙な場合は、 担当者に確認する、 審査済みの代替案を提案する、 またはレビュー済みのサブセットをベンダリングして固定してください。 推測で採用しないでください。
審査に合格した後
- 正確なバージョンを固定してください — 審査したばかりのものに、範囲指定のバージョンを使わないでください。
- 整合性ハッシュを含めてロックファイルをコミットしてください。
- エコシステムが対応している場合は、ハッシュ/SHA で固定した参照を優先してください。
- 確認した内容を記録してください — CVE の状態、Socket の判定、推移的依存関係の数、サンドボックスでの観察結果などです。 PR に 1 行記載すれば、次の担当者が再監査する手間を省けます。
- 使用範囲を最小限に保ってください。 依存関係を減らし、特に直接依存を減らしてください。
ツールのインストール
可能であれば、プロジェクトの mise スキルを使って osv-scanner、snyk と socket の CLI、
およびネイティブの監査ツール
(pip-audit、govulncheck、cargo audit、OWASP dependency-check)をインストールしてください。
それ以外の場合は、references/services.md に従ってください。
CLI が 1 つ不足していても、監査を中断しないでください。
OSV.dev、security.snyk.io、github.com/advisories
はインストールなしで通常の HTTP 経由で確認できます。
また、Docker でステージ 4 に対応できます。
Socket は例外です —
socket.dev はブラウザーにしか応答せず(エージェントには 403 を返すため)、
自動実行には Socket CLI(トークン付き)が必要です。
そうでなければ、そのステージを人間の判断に委ねてください。
403 は「何も見つからなかった」という意味ではありません。
Docker はステージ 4 の唯一の必須要件です。
利用できない場合はその旨を伝え、
ホストにインストールして分離環境を偽装してはいけません。
エコシステムがリストにない場合
5つのステージは言語に依存せず、
変わるのはツールだけです。
パッケージのエコシステムが
references/ecosystems.md に記載されていない場合――
新しい言語や、ニッチなパッケージマネージャーの場合でも――
監査を省略してはいけません。
また、ホストへのインストールに切り替えてもいけません。
記載されているエコシステムをひな形として、
同じ5つのステージを進め、
同等のツールに置き換えてください。
- 識別情報:
deps.dev が対応していない場合は、
エコシステム独自のレジストリ
(
pub.dev、hex.pm、…)で、 メンテナー、ダウンロード数、リリース頻度、yank 状態を確認します。 - 既知の脆弱性:
エコシステムのロックファイルに対して
osv-scannerを実行します(複数のエコシステムに対応しています)。 エコシステム独自の監査ツールがあればそれも使い、 OSV.dev / GitHub Advisories / Socket / Snyk の対応状況も確認します。 正確なスラッグを先に確認してください: サービスによってエコシステムの表記が異なります (OSV ではPub/Hex、GitHub ではpub/erlang)。 スラッグが拒否されたり誤っていたりすると、結果は何も返されません。 これはクリーンな結果ではなく、スキャンが実行されていないだけです。 正規の文字列はreferences/services.mdに記載されています。 - サンドボックス(ステージ 4):
そのツールチェーン用の使い捨て可能な公式ベースイメージを使用し、
記載されているエコシステムと同じ強化フラグ
(ホストマウントなし、ケイパビリティの削除、PID 数とメモリの上限設定)を使い、
同じ2つのパスを実施します。
パス A では
--network bridge上で偵察を行い、 パス B ではコンテナのデタッチとdocker network disconnectを組み合わせて動作を検証します。 インストール、依存関係ツリー全体の列挙、 インストール時の挙動の観察には、エコシステム独自のコマンドを使います。
言語が何であっても、手法は変わりません。 クリーンなコンテナ、 ホストマウントなし、 解決、 依存関係ツリー全体の列挙、 スキャン、 観察です。 あるステージのツールが本当に見つからない場合は、 対応できていないステージを明示してオペレーターに確認してください。 黙って省略してはいけません。 「この言語用のツールがない」ことを、何も調べずに採用する口実にしてはいけません。
対象範囲
- すべてのエコシステム: js / ts、go、python、kotlin / java / jvm、rust、ruby、php、.net、 swift、dart ―― 例外はありません。
- 直接依存と間接依存: リスクが間接依存に潜んでいるからこそ、ステージ 4 があります。
- すべての依存関係の種類: prod、dev、test、build-tool、plugin。
- 新規採用とメジャーアップデート: すでに監査済みの依存関係のパッチアップデートは対象外です。
- 標準ライブラリと、 信頼できるソースのファーストパーティ内部パッケージは対象外です。
報告
監査後、次の内容をまとめます。
- 監査したパッケージとバージョンの正確な情報。
- ステージ 0 の識別情報(実際のパッケージ、メンテナー、ライセンス)。
- ステージ 1 / 3 の既知の脆弱性の結果(深刻度別の CVE。修正可能かどうか)。
- ステージ 2 の Socket アラート。
- ステージ 4 の依存関係ツリーの規模、 注目すべき、またはリスクのある間接依存関係、 サンドボックスで観察された内容。
- 判定:
採用(
X.Y.Zに固定)/却下(理由)/オペレーターにエスカレーション。