
npm v12 shipped on July 8 and it is now the default registry client. Two changes landed together: install-time lifecycle scripts are off by default for the first time in npm’s 16-year history, and GitHub announced the deprecation schedule for 2FA-bypass granular access tokens. The install change will break CI pipelines silently. The token change has a hard August deadline. You need to handle both.
Install Scripts Are Off — and CI Will Lie to You
The core change in npm 12: npm install no longer executes preinstall, install, or postinstall lifecycle scripts from dependencies unless you have explicitly allowed them. Git dependencies and remote-URL tarballs also require new flags to resolve. The immediate risk is not that your install will fail. It is that it will not.
npm ci exits with code 0 even when scripts are blocked. Your pipeline turns green. No error. But native binaries are not compiled and binary downloaders never ran. The crash happens in production when your app actually tries to use sharp, bcrypt, or Puppeteer. That is the design flaw here — silent success is the worst kind of failure signal.
Common packages that silently fail: sharp, bcrypt, canvas, sqlite3, fsevents, esbuild, @tailwindcss/oxide, and any browser-binary downloader like Puppeteer or Playwright. The npm team also closed the “Phantom Gyp” vector: packages with a binding.gyp file used to get an implicit node-gyp rebuild without any explicit lifecycle script. That is blocked too.
Three Commands to Get Your Allowlist Right
The new npm approve-scripts command manages an allowScripts field in your package.json. Here is the sequence:
# Read-only audit — see everything that would be blocked
npm approve-scripts --allow-scripts-pending
# Interactive approval
npm approve-scripts
# Explicit denial for packages you do not trust
npm deny-scripts <package-name>
The resulting allowScripts entry in package.json:
"allowScripts": {
"sharp@0.33.5": true,
"esbuild@0.24.2": true,
"bcrypt@5.1.1": true
}
Approvals are pinned to the exact installed version. Upgrading sharp from 0.33.5 to 0.33.6 means re-approving. For the fastest safe start on an existing project: run npm approve-scripts --all to approve everything currently in your tree, commit the result, then tighten it over time. Add a smoke test after npm ci that requires the native module — do not rely on exit codes alone.
Your 2FA-Bypass Tokens Have an August Deadline
Granular access tokens (GATs) configured with bypass2fa=true will start losing permissions in August 2026. After that date, these tokens cannot create or delete other tokens, cannot change account password, email, or 2FA configuration, cannot modify package maintainers or trusted publishing configuration, and cannot manage org or team membership.
In January 2027, they lose direct publish rights entirely. Publishing via a 2FA-bypass token will only be able to stage a release — a human with 2FA will need to approve before it goes public.
If your CI pipeline uses a long-lived npm token for automated publishing, the migration path is OIDC Trusted Publishing. You need id-token: write permission in your GitHub Actions workflow, npm CLI 11.5.1 or later, and Node 22.14.0 or higher. No long-lived secret stored in CI.
Why npm 12 Happened: The Miasma Worm
The Miasma supply chain attack in June 2026 compromised 32 packages under the @redhat-cloud-services namespace — roughly 80,000 weekly downloads. Attackers abused GitHub Actions OIDC to publish malicious versions, then used preinstall hooks to harvest CI credentials: GitHub tokens, npm tokens, SSH keys, AWS and GCP secrets. A later variant used a 157-byte binding.gyp file instead — bypassing every security check that looked for explicit script entries. GitHub called install-time scripts “the single largest code-execution surface in the npm ecosystem.”
pnpm has had an onlyBuiltDependencies allowlist for years. Deno has always required explicit permission flags. Bun v1.x still executes install scripts by default — a meaningful security differentiator now that npm has changed its position.
What to Do Now
- Run
npm approve-scripts --allow-scripts-pendingin every project that uses native addons or binary downloaders. Commit the resultingallowScriptsfield. - Add a post-install smoke test to CI that actually requires your most critical native module. Do not trust a clean exit code.
- Audit every CI workflow that publishes to npm. If it uses a GAT with
bypass2fa=true, migrate to OIDC Trusted Publishing before August.
Supply chain hygiene is no longer something you bolt on after an incident. npm just made the default position explicit. Now the question is whether your allowlist and your CI pipeline reflect what you actually trust.













