codingsalt

npm v12 Disables Install Scripts: Migration Guide

npm v12 shipped July 8, 2026 with install scripts off by default. Here's what silently breaks in CI, and the migration steps to fix it.

CodingSalt Editorial5 min read

npm v12 shipped on July 8, 2026, and it flips three long-standing defaults from allow to deny: dependency lifecycle scripts (preinstall, install, postinstall, prepare), git dependencies, and dependencies fetched from remote URLs no longer run or resolve automatically. Each now needs an explicit allowlist entry, and the change is retroactive — it applies the moment a project upgrades, not just to newly added dependencies.

What actually flipped

Three separate defaults changed, each shipped on its own timeline before converging in v12:

  • allowScripts defaults to off. preinstall, install, postinstall and prepare scripts from dependencies — including implicit node-gyp rebuilds for native modules — are skipped unless the package has a matching entry in package.json's allowScripts field.
  • --allow-git defaults to none. Git dependencies, direct or transitive, no longer resolve without explicit approval. This one was announced back on February 18, 2026 and has been available since npm 11.10.0.
  • --allow-remote defaults to none. Dependencies pulled from remote URLs, such as HTTPS tarballs, need the same explicit allowance. Available since npm 11.15.0.

--allow-file and --allow-directory are unchanged — local, filesystem-based dependencies still work the way they always did.

The trap: CI stays green with nothing installed

The change that catches teams off guard isn't the new default itself, it's what happens when a blocked script is silently skipped instead of causing a failure. By default, npm ci prints a warning and moves on — exit code 0 — even when a dependency's postinstall step was supposed to compile a native binary that your application needs at runtime. The build looks successful. The binary simply isn't there.

npm ci
# npm warn Skipping install scripts for "sharp" (no allowScripts entry)
# ✓ added 412 packages in 8s
# exit code: 0 — but sharp's native binary was never built

npm ships a fix for exactly this: the strict-allow-scripts config converts skipped scripts into hard errors instead of warnings. It's off by default — turning it on is a config change teams have to make deliberately, in CI specifically, since a hard failure during local development is more disruptive than useful.

Migrating without guessing

The safest path is to do the discovery work before v12 becomes the default in your project, using the warnings that npm 11.16.0 already prints:

npm install -g npm@11.16.0
npm install                        # surfaces every script v12 would block
npm approve-scripts --allow-scripts-pending   # lists pending packages

Review the list, then approve what you trust and record the rest as denied so the decision is explicit and versioned:

npm approve-scripts sharp          # approve one package, pinned to its version
npm approve-scripts --all          # approve everything currently pending
npm deny-scripts fsevents          # explicitly block a package's scripts

npm approve-scripts is an alias for npm install-scripts approve, and by default it writes version-pinned entries (sharp@0.34.2) rather than name-only ones — a script that was safe in one version doesn't stay auto-approved after an upgrade, which is the intended behavior for a security control like this rather than a rough edge to work around.

{
  "allowScripts": {
    "sharp@0.34.2": true,
    "fsevents": false
  }
}

That block lives in package.json and travels with the repository, so the next developer — or the next CI run — inherits the same allowlist instead of re-discovering it.

Watch optional, platform-specific dependencies

One gap worth testing for directly: an npm/cli issue reported npm ci --strict-allow-scripts rejecting fsevents, a macOS-only optional dependency, even though npm approve-scripts --allow-scripts-pending never flagged it as needing review on the Linux machine where the check ran. Optional dependencies that only install on specific platforms can fall through the gap between what the approval tool sees and what strict enforcement checks at install time. If your pipeline runs on more than one operating system, verify the allowlist behavior on each of them rather than assuming a Linux CI run covers a package that only installs on macOS.

Why now

The timing isn't arbitrary. Install-time script execution has been the delivery mechanism for a run of JavaScript ecosystem supply-chain attacks over the past year, precisely because a postinstall script runs automatically, with the same permissions as the developer or CI job running npm install, before anyone reviews what the package actually does. Removing that automatic execution path doesn't stop a malicious package from being published, but it does stop it from running unattended the moment someone installs it — the same shift toward reviewed, versioned trust that's shown up elsewhere in the ecosystem, like Next.js's move to scheduled, disclosed security releases instead of ad hoc patches.

Before your team upgrades

  1. Upgrade to npm 11.16.0+ first, not straight to v12, so you see the warnings without the enforcement.
  2. Run npm approve-scripts --allow-scripts-pending in every package and workspace — the command is workspace-unaware, so it needs to run in each one individually.
  3. Commit the resulting allowScripts block in package.json so the allowlist is reviewed in the same pull request as the dependency that needs it.
  4. Turn on strict-allow-scripts in CI only, after the allowlist is populated, so a missing entry fails the build loudly instead of shipping a broken artifact quietly.
  5. Test the allowlist on every OS your pipeline runs on before relying on strict mode, given the platform-specific dependency gap above.

Teams already tracking dependency and CVE hygiene as part of a monthly patch cadence — the kind of process GitHub Models' retirement notice also rewarded by giving weeks of brownout warning instead of a surprise cutoff — will find this migration is mostly a matter of doing the npm approve-scripts pass early rather than discovering the gap in a failed production deploy.

Frequently asked questions

What exactly changed in npm v12?

Three defaults flipped from allow to deny: allowScripts (preinstall, install, postinstall and prepare scripts, plus implicit node-gyp rebuilds), --allow-git (git dependencies), and --allow-remote (dependencies fetched from remote URLs like HTTPS tarballs). All three now require an explicit allowlist entry instead of running automatically.

Why does my CI stay green even though a native module never got compiled?

By default, npm skips a blocked lifecycle script with a warning rather than failing the install, so `npm ci` exits 0 even when a package's postinstall build step never ran. Setting the `strict-allow-scripts` config turns those skips into hard errors, which is what npm's own guidance recommends for CI.

How do I migrate without guessing which packages need scripts?

Upgrade to npm 11.16.0 or later first — it prints the same blocking warnings v12 enforces, without actually blocking anything yet. Run your normal install, then `npm approve-scripts --allow-scripts-pending` to list every package with a pending script, review each one, and commit the resulting `allowScripts` entries in package.json.

Do optional, platform-specific dependencies need special handling?

They can be a gap in strict enforcement. An npm/cli issue reported `npm ci --strict-allow-scripts` rejecting the macOS-only `fsevents` package even though `npm approve-scripts --allow-scripts-pending` never flagged it as needing review, since the tools can disagree on optional dependencies that only install on some platforms. Test your strict CI config on every OS your pipeline actually runs on, not just the one you developed against.

Sources

  1. Upcoming breaking changes for npm v12 (GitHub Changelog)
  2. npm-install-scripts (npm CLI docs)
  3. npm ci with strict-allow-scripts rejects package approve-scripts cannot see (npm/cli issue #9562)
  4. npm v12 ships with install scripts off by default (Socket.dev)
4 min read

Gemini 3.6 Flash Pricing: What Changes for Developers

Gemini 3.6 Flash cuts output pricing to $7.50/1M tokens and uses 17% fewer tokens per task. Here is the real cost math and what to check before migrating.

  • AI
  • Developer Tools
  • Software Engineering