ブログ一覧

Dependabot の PR の差分では乗っ取りを見抜けない

更新: Web開発者向け

GitHub の依存更新ボット Dependabot の PR は、CI が通れば merge してよいのでしょうか。AI エージェントや人が差分を読めば安全でしょうか。乗っ取られた版は差分を読んでも見抜けないので、公開から 7日たたない版を入れない、install のときに依存のコードを動かさない、GitHub Actions をコミットの SHA で固定する、の 3つで守っています。その上で minor と patch は CI 任せにし、major だけをリリースノートで読んでいます。

私は個人で複数のリポジトリを運用し、更新 PR の merge を AI エージェント(Claude Code)に任せています。この記事に出てくるのは、pnpm を使うモノレポと、npm を使い GitHub Actions から AWS にデプロイするリポジトリの 2つで、どちらも main への merge がそのまま本番へのデプロイです。Dependabot を有効にした直後、pnpm のモノレポでは脆弱性のアラートが 39件開き、npm のリポジトリには更新の PR が 8本たまりました。

PR の差分に出るのは版番号とハッシュの変化だけで、乗っ取られた版は見抜けない

想定している攻撃は、メンテナのアカウントが乗っ取られて悪意ある版が公開され、数時間から数日で取り下げられる、という型です。Dependabot は新しい版が出た直後にその版を提案します。

PR に出るのは、版番号と、lockfile に記録された配布物のハッシュの変化です。配布物の中身が難読化されていれば、人が読んでもエージェントが読んでも見抜けません。

1つ目の守りは、公開から 7日たたない版を入れないこと

pnpm の minimumReleaseAge(公開から指定の時間がたっていない版を解決しない設定)に、7日にあたる 10080 を入れています。pnpm 10.16 で入った設定で、単位は分です。Dependabot の PR だけでなく、エージェントが作業中に打つ pnpm add も、間接依存の版も含め、あらゆる install に適用されます。pnpm の公式ドキュメントには、悪意ある版は多くの場合 1時間以内に見つかってレジストリから消されるとあり、この設定は待っている間に悪意ある版が消されることを前提にしています。

Dependabot の cooldown(公開から指定日数たっていない版を提案しない設定)も 7日にしていますが、補助として使っています。cooldown が適用されるのは Dependabot の提案のうち新しい版への通常の更新(version updates)だけで、脆弱性の修正版への更新(security updates)には適用されません。

脆弱性の修正は cooldown で待たされないので、修正の PR はすぐに来ます。その修正版が minimumReleaseAge に止められないかが気になりますが、実際に入れた修正版はどれも公開から 7日以上たっていました。protobufjs は 19日、ws は 39日、path-to-regexp は 167日です。

minimumReleaseAge は、Dependabot が lockfile を解決し直すときにも適用されます。公開から 7日たたない版を、設定をその場だけ外して(--config.minimumReleaseAge=0)意図して入れると、その版が 7日を越えるまで、Dependabot による pnpm の依存の更新は、脆弱性の修正も含めて失敗します。私は設定を緩めず、この挙動を設定ファイルのコメントに書きました。

npm には minimumReleaseAge にあたる設定がありません。日付を指定する --before はありますが、「7日前」のような相対の指定はできません。npm のリポジトリで新しすぎる版を待てるのは Dependabot の提案(cooldown)だけで、エージェントや人が手で打つ install は待ちません。npm のリポジトリの守りは、cooldown と、次の install スクリプトの停止の 2つです。

2つ目の守りは、install で依存のコードを動かさず、デプロイの鍵を install の後で取ること

install のときに依存の postinstall などのスクリプトが走ると、依存のコードがその場で実行されます。pnpm 10 は、依存のこうしたスクリプトを既定で実行しません。pnpm のモノレポで実行を許しているのは、better-sqlite3、esbuild、sharp、workerd の 4つだけです。

npm のリポジトリは、.npmrc の ignore-scripts で止めています。install スクリプトを持つ画像処理のライブラリ sharp がスクリプトを止めても動くことは確かめました。

npm のリポジトリでは、install の順番にも問題がありました。GitHub Actions は OIDC(鍵を置かずに一時的な認証を受け取る仕組み)で AWS のデプロイ用ロールを引き受けてデプロイしています。構成は GitHub Actions の OIDC で AWS の鍵を受け取ってデプロイする方法に書きました。

そのデプロイは「AWS の認証 → npm ci」の順で、悪意ある postinstall がデプロイ用ロールの権限で動けました。いまは、install → lint / typecheck / test → build → AWS の認証 → terraform apply の順です。

3つ目の守りは、GitHub Actions をコミットの SHA で固定すること

タグは書き換えられ、2025年3月にはtj-actions/changed-files のタグが悪意あるコミットに向け直され、ワークフローのログに Secret が出力されました(GitHub Advisory)。

GitHub のセキュリティ強化のガイドも、サードパーティのアクションはコミットの SHA で固定する形を挙げています。

固定は、各アクションの vN タグが指すコミットを git ls-remote --tags で引き、同じコミットを指す vN.x.y タグがあることを機械的に確かめてから行いました。版は変えていません。固定した SHA の更新も、Dependabot が PR で出します。

minor と patch は CI 任せにして、major は 1本ずつ読む

minor と patch は、Dependabot の groups の設定で週 1本の PR にまとめ、CI が通ればエージェントが merge します。major は 1本ずつの PR にし、エージェントがリリースノートを読んで必要な修正をしてから merge します。main への merge が本番へのデプロイなので、自動 merge は入れていません。

それでも、CI でもリリースノートでも見つけられなかった不具合が 2回あり、どちらも設計を変えました。

major の更新で、main への push でしか走らないリリースのワークフローが壊れた

Changesets(変更履歴とリリースを管理するツール)を GitHub Actions で動かす changesets/action を、v1 から v2 に上げる PR を 2つのリポジトリで merge しました。入力名の変更はリリースノートの対応表どおりに直し、CI も通りましたが、merge の直後に 2つのリポジトリでリリースのワークフローが失敗しました。v2 は Changesets の CLI v3 が前提で、どちらのリポジトリも CLI v2 でした。リリースのワークフローは main への push でしか走らないので、失敗は PR の CI には出ません。

リリースのワークフローの changesets/action の行だけを v1.9.0 に戻し、約 20分で復旧しました。v2 へは、CLI を v3 に上げる PR で一緒に上げます。main への push でしか走らないワークフローは、PR の CI が通っても検証されていません。

minor の更新で、Dependabot が知らない lockfile の版だけが古いまま残った

npm のリポジトリで、sharp を 0.33.5 から 0.35.4 に上げるまとめ PR を merge したところ、デプロイがビルドの段で止まりましたが、terraform apply の前だったので本番は動き続けました。

sharp はネイティブのバイナリを含み、1つのファイルにバンドルできません。そのため、デプロイする成果物のディレクトリにだけ別に install し、そのための lockfile をリポジトリの lockfile とは別に持っていましたが、Dependabot はこの lockfile を知りません。依存の版が 2つの lockfile に書かれ、リポジトリの lockfile の版だけが自動で上がる構造でした。sharp は使っていない画像の分割機能のためだけに残っていたので、機能ごと消しました。Dependabot が更新しないファイルに依存の版が書かれていると、Dependabot の PR は自分が知っているファイルの版だけを上げ、CI が通ってもデプロイで壊れます。

残る危険は、7日以上見つからなかった悪意ある版が本番の鍵に触ること

公開から 7日たっても見つからなかった悪意ある版が、本番の鍵に触る危険は残ります。配布物を自前で監査しない限りこの危険は無くならず、個人で運用する規模に配布物の監査は合わないので、この危険を受け入れています。

この記事をシェア