はじめに
供給チェーンシリーズでは、cooldownは「新しく公開された悪意あるバージョン」を防ぐ時間ベースのゲートであることを解説しました。一方yarn npm auditはすでに依存ツリーに入っている既知のCVEを検出します。
その間にあるもう1つの盲点があります — 開発者がPRで新しい依存関係を追加する瞬間です。この記事では、GitHubのdependency-review-actionでこの段階を自動検証する方法を解説します。
どんなリスクがあるか
PRで新しい依存関係を追加するときに起きうることは次のとおりです。
1. 既知のCVEを持つパッケージを取り込む場合
yarn add some-cool-libを実行したとき、そのライブラリ(またはその間接依存関係)にすでにhighランクのCVEが登録されている場合があります。レビュアーがCVEデータベースを毎回直接確認しなければ、そのままマージされます。
2. タイポスクワッティングパッケージを誤って追加
yarn add react-doomのようなタイポ。または意図的に人気パッケージに似た名前の偽パッケージがnpmに公開されていて、うっかりそれをインストールしてしまう場合です。
3. 依存関係コンフュージョンによる内部パッケージのなりすまし
内部プライベートパッケージと同じ名前の偽パッケージが公開レジストリに公開され、ビルドシステムが誤って取得してしまう場合です。Alex Birsanの2021年の研究では、Microsoft・Apple・Uberなど35社以上でコード実行が確認された攻撃手法です。
4. ライセンス違反
GPL/AGPLのような強いコピーレフトライセンスのパッケージを商用SaaSに無意識に導入すると、会社レベルの法的リスクになります。この確認も人が毎回行うのは困難です。
5. メンテナーの変更
同じパッケージでも、メンテナーが変わってから新バージョンが出る場合、信頼の基盤が変わります。2018年のevent-stream事例のように、新しいメンテナーが意図的に悪意あるコードを挿入した前例があります。
cooldownはこのうち一部(悪意ある新規公開)しか防げず、残りはPR段階での自動検証が必要です。
実際の事例: タイポスクワッティングと意図しないリスク
crossenvとcross-env(2017年)
最もよく引用されるタイポスクワッティングの事例です。正規パッケージcross-envの代わりに1文字異なるcrossenvをインストールすると、環境変数とシステム情報を外部に漏洩する悪意あるコードが実行されました。(公式分析)
この種のパッケージは公開後比較的早くnpmから削除されますが、その間に誰かのPRで1度入り込めば終わりです。Dependency Review ActionをPRゲートに設けておけば、このようなパッケージが依存関係グラフに追加される時点でアラートが出るようにできます。
Alex Birsanの依存関係コンフュージョン(2021年)
Alex BirsanはMicrosoft、Apple、Uberなど35社以上のビルドシステムでコード実行に成功しました。方法はシンプルでした — 内部プライベートパッケージの名前を推測またはGitHubから収集した後、公開npmに同じ名前のパッケージをより高いバージョンとして公開。するとビルドシステムが公開バージョンを優先して取得しました。
この攻撃の本質は「入るべきでないパッケージが依存関係グラフに新たに追加される」という点です。Dependency Review ActionはPRの差分から依存関係グラフの変更を自動検知するため、このような新規追加を最も早く捕捉できる地点になります。
Dependency Review Actionの動作方式
actions/dependency-review-actionはPRのbaseブランチとheadブランチの依存関係グラフの差分を計算した後、追加または変更された依存関係について以下を検査します。
- 既知のCVE(GitHub Advisory Databaseとのマッチング)
- 深刻度(severity) —
low/moderate/high/critical - ライセンス(
allow-licenses/deny-licensesのホワイトリスト/ブラックリスト) - (オプション)OpenSSF Scorecard スコア — メンテナーの活動状況・コードレビュー頻度など
設定した閾値を超える変更があるとPRが失敗し、PRのコメントにどの依存関係がどの理由でブロックされたかが自動で表示されます。
どのように適用するか
最もシンプルなワークフローは次のとおりです。
# .github/workflows/dependency-review.yml
name: Dependency Review
on: pull_request
permissions:
contents: read
pull-requests: write # PRにコメントで結果を報告
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<SHA> # v6
- uses: actions/dependency-review-action@<SHA>
with:
fail-on-severity: high
comment-summary-in-pr: always
オプションの意味:
| オプション | 説明 |
|---|---|
fail-on-severity | このランク以上のCVEが追加されるとPRが失敗。low / moderate / high / critical |
comment-summary-in-pr | alwaysにするとPRごとに自動でサマリーコメントが追加される |
allow-licenses / deny-licenses | 許可/禁止ライセンスのリスト |
推奨の初期設定
初回導入時は次の組み合わせがコスト対効果に優れています。
fail-on-severity: high
comment-summary-in-pr: always
high以上のみブロックすればfalse positiveはほぼなく、コメントで可視化されることでレビュアーが追加される依存関係のリスクを一目で確認できます。運用の安定性が確保できたらmoderateへ段階的に絞っていけます。
限界
- GitHub Advisory Databaseに登録されたCVEのみを検出します。0-dayや登録前の悪意あるパッケージはcooldownやSocketのような行動分析SaaSで補完する必要があります。
- すでにマージされた依存関係は検査対象外です。新しいPRの差分のみを確認します。既存の依存関係に新しいCVEが追加されるケースは
yarn npm auditが検出します。 - GitHub以外のホスティングでは動作しません。GitLab・Bitbucketを使っている場合はSnyk PR Checksのような代替手段を検討する必要があります。
まとめ
PR段階は供給チェーン防御における最後の人間によるチェックポイントです。その地点で依存関係変更のリスクを人が毎回直接判断するのは現実的に困難です。Dependency Review Actionはその判断を自動化し、レビュアーが「このPRで追加される新しい依存関係に既知のCVEがあるか?」を別途調べなくて済むようにします。
ワークフロー1つ、オプション2つで完結します。適用コストに対してブロックできるインシデントの範囲が広いため、供給チェーン防御シリーズの3つの戦略に次いで導入しやすい4つ目の層です。
参考資料
- GitHub Dependency Review Action公式リポジトリ
- About Dependency Review(GitHub Docs)
- Alex Birsan, “Dependency Confusion”
- crossenv malware on the npm registry(npm公式分析)
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Dekuが開発したアプリを使ってみてください。Dekuが開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。