npm 12のinstallスクリプト停止|壊れる依存と許可の決め方


npm 12のinstallスクリプト停止とは、依存パッケージの preinstall・install・postinstall を、package.json の allowScripts で許可したものだけ実行する、npm 12からの既定の動きです。許可していない依存のスクリプトは飛ばされ、警告が出るだけで npm install 自体は成功します。

困るのは、この「成功するのに一部が動いていない」状態です。インストールの段階では何も失敗せず、PuppeteerでChromeを起動したときや、Prismaのクライアントを読み込んだときに初めてエラーになります。CIが通っていても、本番のジョブや別の開発者の手元で壊れる、という形で表に出ます。

この記事では、よく使う9つのパッケージをnpm 12で入れて「止まっても動くもの」と「動かなくなるもの」を実測した結果を載せます。そのうえで、許可・拒否・別の手段の決め方、移行の手順、CIで黙って壊れるのを防ぐ設定、pnpm・Yarn・Bunとの違い、移行後に確かめる項目までをまとめます。検証は2026年11月2日に、macOS(Apple Silicon)上のNode.js 24.22.2で、npm 12.2.1・npm 11.19.1・pnpm 10.20.0/12.16.1・Yarn 4.18.1を使って行いました。公式ドキュメントも同じ日に確認しています。


結論:installは成功する。壊れるのは「あとで動かしたとき」

npm 12への移行で最初にやることは、installスクリプトを持つ依存を一覧にし、1つずつ「許可」「拒否」を決めて package.json に書き、CIでは未判定の依存があれば失敗させることです。既定のままだと、判定していない依存は警告だけで飛ばされます。

GitHubの公式発表(GitHub Changelog・2026年7月8日)によると、npm 12は2026年7月8日に正式版になり、latest になりました。既定で変わったのは次の3つです。

項目npm 11までnpm 12の既定許可のしかた
依存のinstallスクリプト(preinstall・install・postinstall、暗黙のnode-gypビルド)実行するallowScripts で許可したものだけ実行npm approve-scripts で package.json に記録
git参照の依存取得する取得しない(allow-git の既定が none).npmrc かコマンドで --allow-git
URL(tarball)参照の依存取得する取得しない(allow-remote の既定が none)。レジストリと同じホストのtarballは対象外.npmrc かコマンドで --allow-remote

自分のプロジェクトの package.json に書いた scripts(npm run build など)は、これまでどおり動きます。止まるのは、依存パッケージがインストールの途中で実行するスクリプトです。

なお、2026年10月下旬の時点で、Node.jsのリリース版(CurrentとLTS)に同梱されているnpmはまだ11系です(nodejs.orgの配布一覧で、Node.js 24.22.2は11.19.1、26.13.0は11.20.0)。npm 12は npm install -g npm@12 などで自分で入れたときに使われます。CIのイメージやチームの誰かが先にnpm 12へ上げた時点で挙動が変わるので、npm 11のうちに準備しておくのが安全です。npm 11.16.0以降は、同じ判定を警告として表示します(実測でも11.19.1で警告が出ました)。


実測:止めても動くパッケージ、動かなくなるパッケージ

npm 12.2.1で、9つのパッケージ(installスクリプトを持つ8つと、比較用にinstallスクリプトの無いbetter-sqlite3)を許可なしで入れ、動かして確かめました(core-jsはスクリプトの中身を読んで確認)。どれも npm install は終了コード0で成功し、installスクリプトを持つものには最後に「install scripts blocked」の警告が出ます。

$ npm install esbuild@0.25.11 sharp@0.34.4
added 8 packages, and audited 9 packages in 1s
npm warn install-scripts 2 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts   esbuild@0.25.11 (postinstall: node install.js)
npm warn install-scripts   sharp@0.34.4 (install: node install/check.js)
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.

そのあと、それぞれのパッケージを実際に使いました。結果は次のとおりです。

パッケージ(版)止まったスクリプト止めたまま使った結果判断
esbuild 0.25.11postinstall(node install.js)TypeScriptの変換が動いた拒否してよい
sharp 0.34.4install(node install/check.js)PNGの生成が動いた拒否してよい
fsevents 2.3.3install(node-gyp rebuild)フォルダの監視が動いた拒否してよい
workerd 1.20261101.1(wrangler経由)postinstall(node install.js)workerd --version が動いた拒否してよい
bcrypt 6.0.0install(node-gyp-build)ハッシュの生成が動いた拒否してよい
better-sqlite3 13.0.3(installスクリプトなし)メモリ上のDBで問い合わせが動いた対象外
core-js 3.51.0postinstall中身はお礼と寄付のメッセージを出すだけ(スクリプトを読んで確認)拒否してよい
puppeteer 25.16.0postinstall(node install.mjs)起動時に「Could not find Chrome」で失敗許可するか、Chromeを別の手順で入れる
prisma/@prisma/client/@prisma/engines 6.19.3preinstall・postinstall読み込み時に「Cannot find module ‘.prisma/client/default’」で失敗許可する

動いたパッケージの多くは、OSごとのビルド済みバイナリを optionalDependencies や同梱ファイルで配っていて、installスクリプトは確認や再ビルドの予備に使っているだけでした。一方、Puppeteerはインストール時にChromeをダウンロードし、Prismaはクライアントを生成しエンジンを取得します。インストール時に外から何かを取ってくる・生成するタイプだけが、止めると壊れます。

プロジェクト単位でも試しました。npm create vite@latest で作ったReactのテンプレート(vite 8.3.5)をnpm 12で npm install すると、止まったのはfsevents(node-gyp rebuild)の1件だけで、vite build はそのまま成功しました。vite 8の依存にはesbuildが入っておらず、代わりに入るrolldownはinstallスクリプトを持っていませんでした。

ただし、この表はmacOS(Apple Silicon)での結果です。ビルド済みバイナリが無いOSやCPUでは、node-gypのビルドが必要になり、結果が変わることがあります。判断は、CIと本番と同じOSで実際に動かして確かめてください。確かめ方は後の「移行後に確かめる項目」の節にまとめています。


手元の22プロジェクトで、installスクリプトを持つ依存を数えた

どれくらいのプロジェクトが影響を受けるのかを見るために、筆者の手元にある22個の package-lock.json(Webアプリ・社内ツール・Cloudflare Workersのプロジェクトなど)を集計しました。lockfileでは、installスクリプトを持つ依存に hasInstallScript: true が付いています。

パッケージ含んでいたlockfileの数(22個中)
unrs-resolver10
esbuild8
fsevents8
sharp7
prisma・@prisma/client・@prisma/engines各2
workerd2
core-js2
@sentry/cli・msw・protobufjs・@google/genai・fs-ext-extra-prebuilt各1

22個のうち19個が、少なくとも1つのinstallスクリプト付き依存を持っていました。ほとんどのプロジェクトで、npm 12に上げると何らかの警告が出ます。ただ、壊れる側(Prisma)を含んでいたのは2つだけで、残りは警告が出ても動くものが中心でした。なお、unrs-resolverは最新の1.13.0ではinstallスクリプトが無くなっていて、手元の古いlockfileにだけ残っていました。依存を更新すると対象が減ることもあります。

自分のプロジェクトで数えるには、プロジェクトのフォルダで次の1行を実行します。npm 11でも12でも動き、lockfileを読むだけなので何も変更しません。

node -e 'const l=require("./package-lock.json");for(const[k,v]of Object.entries(l.packages))if(k&&v.hasInstallScript)console.log(k.split("node_modules/").pop()+"@"+v.version)'

npm 11.16.0以降なら、npm install-scripts ls でも、まだ許可も拒否もしていない依存を一覧にできます(npm 11.19.1と12.2.1で動作を確認)。


allowScriptsの決め方:許可・拒否・別の手段

判断の軸は「そのスクリプトが、止めると困ることをしているか」です。パッケージの人気や知名度ではなく、スクリプトの中身で決めます。中身は npm install-scripts ls に表示されるコマンド(例:node install.js)を手がかりに、node_modules の中のファイルを読めば確かめられます。

スクリプトがしていること例決め方
インストール時に外部からバイナリやブラウザを取ってくるpuppeteer(Chrome)許可する。またはスクリプトを止めたまま、取得を別の明示的な手順にする(Puppeteerならエラー文が案内する npx puppeteer browsers install chrome)
コードやクライアントを生成するprisma/@prisma/client許可する(版を固定して許可)
ビルド済みバイナリの確認・予備のビルドesbuild・sharp・fsevents・workerd・bcrypt実際のOSで動けば拒否。動かなければ許可
メッセージを出すだけcore-js拒否
中身が読めない・目的が分からない—拒否して、必要になったら中身を読んでから許可

許可は、npm approve-scripts の既定どおり版を固定(pin)して書くのがおすすめです。名前だけで許可すると、将来乗っ取られた版が出たときにも、そのスクリプトが動きます。今回の対策が守ろうとしているのはまさにその場面です。


移行の手順:npm 11のうちに許可リストを作る

次の手順は、npm 11.19.1と12.2.1で実際に通したものです。npm 11.16.0以降なら、npm 12に上げる前から同じコマンドで準備できます。

手順1:未判定の依存を一覧にする

$ npm install-scripts ls
4 packages have install scripts blocked because they are not covered by allowScripts:
  @prisma/client@6.19.3 (postinstall: node scripts/postinstall.js)
  @prisma/engines@6.19.3 (postinstall: node scripts/postinstall.js)
  prisma@6.19.3 (preinstall: node scripts/preinstall-entry.js)
  puppeteer@25.16.0 (postinstall: node install.mjs)

npm approve-scripts --allow-scripts-pending でも同じ一覧が出ます。どちらも読むだけで、package.json は変えません。

手順2:許可と拒否を書く

npm install-scripts approve prisma @prisma/client @prisma/engines
npm install-scripts deny puppeteer

実行すると、package.json に次の欄ができました。許可は版付き、拒否は名前だけで書かれます(公式ドキュメントのとおり、拒否は常に名前だけになります)。

{
  "allowScripts": {
    "prisma@6.19.3": true,
    "@prisma/client@6.19.3": true,
    "@prisma/engines@6.19.3": true,
    "puppeteer": false
  }
}

npm approve-scripts と npm deny-scripts は、npm install-scripts approve/deny の別名です。どちらを使っても同じ欄に書かれます。

手順3:許可したスクリプトをいま実行する

許可を書いただけでは、すでに飛ばされたスクリプトは実行されません。npm rebuild を実行すると、許可した依存のスクリプトだけが動きます。実測では、rebuildのあとに node_modules/.prisma/client が生成され、Prismaのエンジン(libquery_engine-darwin-arm64.dylib.node)も取得されました。拒否したPuppeteerのChromeは取得されませんでした。

手順4:package.jsonをコミットし、npm ciで通るか確かめる

allowScripts はチーム全員とCIで共有する設定なので、package.json ごとコミットします。node_modules を消して npm ci をやり直すと、許可したPrismaのスクリプトだけが動き、拒否したPuppeteerは警告も出さずに飛ばされました。


CIで「黙って壊れる」を防ぐ:strict-allow-scripts

CIでは、未判定の依存が1つでもあればインストールを失敗させる設定にします。既定のままでは、新しく入った依存のスクリプトが警告だけで飛ばされ、その依存を実際に使う処理まで誰も気づきません。

npm 12には strict-allow-scripts という設定があり、true にすると未判定の依存がエラーになります。プロジェクトの .npmrc に1行書けば、ローカルでもCIでも同じ動きになります。

strict-allow-scripts=true

この状態で、未判定の依存があるまま npm ci すると、次のように終了コード1で止まりました(括弧内の表示は条件によって変わることがあります)。

$ npm ci --strict-allow-scripts
npm error code ESTRICTALLOWSCRIPTS
npm error --strict-allow-scripts: 2 package(s) have install scripts not covered by allowScripts:
npm error   esbuild@0.25.11 (install: (install scripts present))
npm error   sharp@0.34.4 (install: (install scripts present))
npm error Approve them with `npm install-scripts approve`, deny them with `npm install-scripts deny`, or bypass this check with `--dangerously-allow-all-scripts`.

明示的に拒否した依存(false)は、strictでもエラーになりません。エラーになるのは「許可も拒否もしていない」依存だけです。エラー文にある --dangerously-allow-all-scripts は、すべてのスクリプトを無条件に動かす移行用の抜け道で、公式ドキュメントも使わないよう強く勧めています。CIの設定に残さないようにします。


版を固定した許可は、更新すると外れる

esbuild@0.25.11 を許可した状態で、npm install esbuild@0.25.12 に上げると、新しい版のスクリプトはまた止まり、strictではエラーになりました。依存を更新するたびに、スクリプトを読み直す機会が自動で来るということです。

$ npm install esbuild@0.25.12
npm warn install-scripts   esbuild@0.25.12 (postinstall: node install.js)

$ npm install-scripts approve esbuild
Approved esbuild:
  removed-stale esbuild@0.25.11
  added esbuild@0.25.12

もう一度 approve すると、古い版の行が消えて新しい版に書き換わりました。CIをstrictにしておけば、依存の更新PRはこの時点で止まります。スクリプトを読み直してから approve し直すと、package.json の allowScripts の1行の差分が「読み直した」記録としてPRに残ります。使わなくなった許可は npm install-scripts prune で消せます(--dry-run で事前に確認できます)。


pnpm・Yarn・Bunとの違い

installスクリプトを既定で止める動きは、npmより先に他のパッケージマネージャーで始まっていました。同じesbuild 0.25.11を入れて比べた結果です(Bunは手元に無いため公式ドキュメントの記載)。

ツール(版)既定の動き止めたときの結果許可の書き場所
npm 11.19.1実行する(警告だけ出す)成功package.json の allowScripts
npm 12.2.1止める成功(警告)。strict-allow-scripts で失敗にできるpackage.json の allowScripts
pnpm 10.20.0止める成功(「Ignored build scripts」の警告)pnpm approve-builds で記録
pnpm 12.16.1止める失敗(ERR_PNPM_IGNORED_BUILDS・終了コード1)pnpm approve-builds で記録
Yarn 4.18.1止める(enableScripts の既定が false)成功(「all build scripts have been disabled」)package.json の dependenciesMeta に "built": true
Bun止める(よく使われるパッケージの一覧だけ既定で許可)—(未実測)package.json の trustedDependencies

Yarnでは、"dependenciesMeta": {"esbuild": {"built": true}} を書いて yarn install し直すと、esbuildのビルドが実行されたことを確認しました。Bunの trustedDependencies は、自分で書くと既定の許可一覧を置き換える(足すのではない)とBunの公式ドキュメントに書かれています。ツールを切り替えるときは、許可の書き方を移し替える必要があります。


移行後に確かめる項目

移行が正しくできたかは、「インストールが通るか」ではなく「依存を実際に使う処理が通るか」で判定します。今回の実測で壊れた2つも、インストールは成功していました。

  1. npm install-scripts ls の出力が空になっている(未判定の依存が残っていない)
  2. .npmrc に strict-allow-scripts=true があり、node_modules を消してからの npm ci が通る
  3. CIと本番と同じOSで、installスクリプトを拒否した依存を1回ずつ実際に使う処理(ビルド・画像処理・テスト)が通る
  4. 許可した依存が生成・取得するもの(Prismaなら node_modules/.prisma/client)が、npm ci のあとに存在する
  5. allowScripts の許可が版付きで書かれていて、--dangerously-allow-all-scripts がCIの設定やスクリプトに残っていない
  6. git参照やURL参照の依存を使っているなら、.npmrc に allow-git/allow-remote を書いたうえで、npm 12で npm ci が通る

3番目のチェックで落ちた依存は、その依存だけを許可して npm rebuild し、もう一度同じ処理を試します。それでも直らないときは、エラー文に従って取得や生成を別の手順として書き出します(Puppeteerなら、ブラウザの取得をCIの1ステップにする)。

依存の中身を確かめる考え方は、AIが書いたコードを受け入れるときの確認とも共通しています。vibe coding とは?原義と実務用法の違い・AIのコードを受け入れる判定手順では、npm audit で既知の脆弱性を確かめるゲートを紹介しています。auditは「既知の脆弱性」、allowScriptsは「インストール時に何が動くか」を見るので、両方を並べて使うのがおすすめです。

Claude Codeなどのエージェントに npm install を任せているなら、エージェントのシェルの中で動くスクリプトの範囲も絞っておくと安心です。設定の考え方はClaude Codeのsandbox設定|denyで止まらない操作を実測にまとめています。


よくある質問

npm 12のinstallスクリプト停止とallowScriptsについて、よくある質問をまとめました。

Q. npm 12に上げると、npm installは失敗しますか?

既定では失敗しません。許可していない依存のinstallスクリプトは飛ばされ、最後に警告が出て、終了コード0で終わります。失敗させたいときは .npmrc に strict-allow-scripts=true を書きます。

Q. 自分のpackage.jsonに書いたscriptsも止まりますか?

止まりません。止まるのは依存パッケージがインストール時に実行する preinstall・install・postinstall などで、npm run build のような自分のスクリプトはこれまでどおり動きます。

Q. ignore-scriptsとallowScriptsは何が違いますか?

ignore-scripts はすべてのスクリプトを一律に止める設定で、allowScripts は依存ごとに許可と拒否を記録する仕組みです。ignore-scripts を有効にすると allowScripts の許可よりも優先され、許可した依存のスクリプトも動きません。

Q. esbuildやsharpは許可しないと動きませんか?

今回のmacOS(Apple Silicon)での実測では、esbuild 0.25.11もsharp 0.34.4も許可せずに動きました。ビルド済みバイナリが用意されていないOSやCPUでは結果が変わることがあるので、CIと本番と同じ環境で実際に使って確かめてください。

Q. npm 11のうちに準備できますか?

できます。npm 11.16.0以降は、npm 12で止まる依存を警告で知らせ、npm install-scripts ls や npm approve-scripts も使えます。npm 11.19.1で、許可リストの作成と一覧の表示が動くことを確認しました。

Q. 許可はパッケージ名だけで書いてもいいですか?

書けますが、おすすめしません。名前だけの許可は、乗っ取られた将来の版のスクリプトも動かしてしまいます。npm approve-scripts の既定どおり版を固定して許可し、更新のたびに読み直すほうが安全です。


次に読む

バックエンド全体のセキュリティの考え方から見直したいときは、バックエンドとは?サーバー・DB・APIの全体像をわかりやすく解説の「セキュリティの基本」の節が入り口になります。Viteの仕組みと導入はViteの基礎知識|高速フロントエンド開発の仕組みと導入方法を解説で解説しています。


関連記事