🔒

pnpm で依存パッケージのサプライチェーン攻撃に備える設定

公開日1分後2026-10-31
MarkdownRaw Content

この記事のポイント

  • 公開から 7 日たっていないバージョンは入れない(minimumReleaseAge)
  • package.json のバージョンは ^ を付けずに固定し、CI は lockfile のとおりに入れる
  • 更新は Dependabot の PR で受け取り、こちらも公開から 7 日待つ(cooldown)
  • pnpm のバージョンも packageManager で固定する

何に備えるのか

npm のパッケージは、作者のアカウントが乗っ取られると、悪いコードを入れたバージョンが公開されることがあります。よく使われるパッケージでも起きていて、インストールしただけで、手元や CI の認証情報(npm や GitHub のトークン、クラウドの鍵など)を盗まれます。

こうしたバージョンは、多くの場合、公開から数時間〜数日で見つかって消されます。危ないのは、公開されてすぐのバージョンを、知らないうちに入れてしまうことです。

次のようなときに起きます。

  • ^1.2.0 のような範囲指定で、pnpm install や pnpm update が新しいバージョンを選ぶ
  • 依存パッケージの依存パッケージ(間接的な依存)が、新しいバージョンに変わる
  • CI が lockfile と違うバージョンを入れる

ここで紹介する設定は、どれも「新しいバージョンは、少し待ってから、目に見える形で入れる」ためのものです。

公開から 7 日たっていないバージョンを入れない

pnpm 10.16 から、minimumReleaseAge で、公開から指定した時間(分)がたっていないバージョンを選ばないようにできます。間接的な依存にも効きます。

# pnpm-workspace.yaml
minimumReleaseAge: 10080 # 7 日

ワークスペースを使っていなくても、pnpm の設定は pnpm-workspace.yaml に書きます。

セキュリティの修正などで、新しいバージョンをすぐに入れたいときは、パッケージ名を一時的に除外します。

minimumReleaseAgeExclude:
  - some-package

この確認は、バージョンを選ぶとき(lockfile を作る・更新するとき)にだけ行われます。lockfile にあるバージョンをそのまま入れるときは確かめないので、設定を入れる前の lockfile に新しいバージョンがあっても、そのまま残ります。

バージョンを固定する

saveExact を有効にすると、pnpm add や pnpm update で、package.json に ^ を付けずにバージョンを書きます。

# pnpm-workspace.yaml
saveExact: true
{
  "dependencies": {
    "astro": "7.4.2"
  }
}

実際に入るバージョンは lockfile で決まりますが、package.json も固定しておくと、入っているバージョンがひと目でわかり、更新は必ず package.json の差分として残ります。

CI では、lockfile と違うものを入れないように、--frozen-lockfile を付けます。lockfile が package.json と合っていなければ、インストールが失敗します。

- run: pnpm install --frozen-lockfile

更新は Dependabot の PR で受け取る

バージョンを固定すると、自分で更新しない限り古いままになります。Dependabot に、週 1 回、更新の PR を作ってもらいます。

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
    cooldown:
      default-days: 7
    groups:
      minor-and-patch:
        update-types:
          - minor
          - patch
  • cooldown で、公開から 7 日たったバージョンだけを PR にします。minimumReleaseAge と日数を合わせておかないと、PR の CI でインストールが失敗します
  • 脆弱性の修正(セキュリティアップデート)には、cooldown は効きません。すぐに PR が作られます
  • マイナーとパッチは 1 つの PR にまとめ、メジャーはパッケージごとの PR にします

PR になるので、どのパッケージがどう変わったかを確認でき、CI が通ってから取り込めます。

pnpm と GitHub Actions も固定する

pnpm 自体のバージョンは、package.json の packageManager で固定します。

{
  "packageManager": "pnpm@10.33.4"
}

pnpm/action-setup は、version を指定しなければ、この値を読みます。手元と CI で同じ pnpm を使えます。

GitHub Actions のアクションも、タグではなくコミットの SHA で指定します。タグは付け替えられるので、アクションが乗っ取られると、同じタグのまま中身が変わることがあります。

- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0

Dependabot に package-ecosystem: github-actions を追加すると、SHA の更新も PR にしてくれます。

これで防げないこと

  • 7 日たっても見つからない、悪いバージョン
  • 以前から入っているパッケージに、もともと含まれている問題

7 日待つのは、多くの人が使って問題が見つかるのを待つ、という考え方です。更新の PR を取り込むときは、変わったパッケージの名前と、大きな変更がないかを見ておきます。

なお pnpm 10 では、依存パッケージの postinstall などのスクリプトは、許可したものしか実行されません。インストールしただけで悪いコードが動く経路を、既定で減らしています。