npm turned off the feature that made most supply chain attacks work, and it will break your build before it saves you
npm 12 shipped on July 8, 2026 and is now the default release. Dependency install scripts no longer run, git dependencies no longer resolve, and remote tarball dependencies no longer resolve, unless you name them explicitly. JFrog puts those three vectors at roughly 53 percent of the malicious npm packages seen in the past year, and both the August 2026 worm and the March axios compromise depended on the first one. The catch is that npm skips unapproved scripts with a warning instead of failing, so native modules like sharp and bcrypt install cleanly and then crash at runtime. Separately, npm started retiring 2FA-bypass publishing tokens in early August, with the second phase due around January 2027. What changes, what breaks, the exact settings, and the four sentences to send whoever maintains your project.
Twelve days ago we published a piece about software supply chain attacks, and the single most effective defence in it was a setting most people had never heard of: stop letting packages run code automatically when they are installed.
That advice is now the default. npm 12 shipped on July 8, 2026, it is generally available and tagged latest, and it turns off three behaviours that have been on since the beginning.
This is the most consequential change to how JavaScript projects get built in years, and it deserves a proper explanation rather than a release-note skim, because the way it fails is worse than the way it breaks.
What actually changed
Three defaults flipped. All three are opt-in now.
1. Dependency lifecycle scripts do not run. The npm changelog puts it plainly: “Dependency lifecycle scripts are now blocked by default unless allowed by the root package’s allowScripts policy.” That covers preinstall, install and postinstall from anything in your dependency tree, and it also covers the implicit node-gyp rebuild that fires on any package containing a binding.gyp file, even when that package declares no install script at all.
2. Git dependencies do not resolve. --allow-git now defaults to none. A dependency pointing at a git URL, whether you added it directly or it arrived four levels down, will not be fetched unless you permit it. The enum accepts all, none or root, and root is the tight setting: your own git dependencies resolve, transitive ones do not.
3. Remote URL dependencies do not resolve. --allow-remote also defaults to none. A dependency specified as an HTTPS tarball is blocked.
None of this arrived without warning. --allow-git has existed since npm 11.10.0, --allow-remote since 11.15.0, and the whole set has been available behind warnings since npm 11.16.0, which is where you should be if you want to see what will break before it does.
Why this matters more than a typical default change
Go back to the two worst npm incidents of this year and look at the mechanism rather than the headline.
On March 31, an attacker took over the npm account of the lead maintainer of axios, a package with roughly 100 million weekly downloads, and published two poisoned versions. The library’s actual code was untouched. The entire attack was one added dependency whose only job was to run a postinstall script that dropped a remote access trojan.
On August 4, a worm variant that researchers call Shai-Hulud and Microsoft tracks as ChainDrop backdoored more than 400 packages in a single morning. Its propagation mechanism was a preinstall script that harvested credentials, including npm publishing keys, and used them to poison the next maintainer’s packages.
Neither attack needed you to import anything. Neither needed you to call a function. Both needed exactly one thing: permission to run code the moment a tarball was unpacked. That permission has been the default since npm existed, and it is now gone.
JFrog’s assessment, cited in InfoQ’s coverage of the release, is that the three vectors npm just closed were involved in roughly 53 percent of the malicious npm attacks observed in the past year. That is not a marginal hardening. That is most of the category.
npm is also, it should be said, last to this. pnpm has offered install-script allowlisting for years and turned the protections on by default in version 10; Yarn added controls in 4.10.0; Bun followed in 1.3. The interesting question is not why npm did this but why it took until 2026, and the answer is that npm has three million packages and an ecosystem that would notice.
The failure mode nobody is talking about
Here is the part that will cost people a weekend.
npm 12 does not fail your install when it skips a script. It warns and continues. Strict enforcement is a separate setting, strict-allow-scripts, which is not the v12 default.
That sounds friendly. It is the opposite. The packages that genuinely need install scripts are the ones that compile or download a binary, and if the compile step is silently skipped, the package installs perfectly, the build goes green, and the application throws at runtime when something first calls into the missing binary. The error you get is not “the install script was skipped.” It is a module-not-found or a symbol-not-found from deep inside a native binding, and it fires in whatever environment you deployed to rather than in the terminal where you ran the install.
The packages that behave this way are not obscure. They are:
- Native modules compiled through node-gyp:
sharp,better-sqlite3,bcrypt,canvas. Note that these need approval even when they declare no install script, because the implicitnode-gyp rebuildcounts. - Browser and binary downloaders: Cypress, Playwright, Puppeteer, Electron. These fetch a large binary during install, and without it the tool exists but has nothing to drive.
- Git hook installers:
husky, which sets up hooks in a lifecycle script. The clean alternative is to move that into an explicitpreparescript that you invoke during bootstrap.
This site runs on Astro and uses sharp for image processing, so this list is not academic to us. Our own package.json names exactly three packages permitted to run build scripts, and we manage that list by hand because pnpm made us. Teams on npm have not had to think about it until now.
The settings, precisely
The config keys are the load-bearing part and they are stable:
| Setting | New default | What it does |
|---|---|---|
allowScripts |
off | Dependency lifecycle scripts and implicit node-gyp builds do not run |
strict-allow-scripts |
off | When on, a skipped script is a hard error instead of a warning |
allow-git |
none |
Accepts all, none or root. true is not valid |
allow-remote |
none |
Blocks HTTPS tarball dependencies |
strict-npmrc |
off | When on, unknown .npmrc keys error instead of warning |
Two traps worth knowing before you start:
ignore-scripts=true takes precedence over the allowlist. If your project set that years ago as a blanket defence, it overrides allowScripts entirely and your approvals will appear to do nothing. Remove it first.
A transitive git dependency forces allow-git=all. There is no way to permit one specific git dependency four levels down; you either allow all of them or you remove the thing that pulled it in. The second option is usually the right one, and finding out you have one is itself useful information.
The command surface shifted during the prerelease, so check npm help on the version you actually have rather than trusting a blog post, including this one. The npm v12 changelog documents the flow as: “After installing, run npm install-scripts approve to record approvals and npm rebuild to execute newly approved scripts.” The migration discussion from the npm team documents npm approve-scripts <pkg> to allowlist a package, npm deny-scripts <pkg> to block one, npm approve-scripts --allow-scripts-pending to list what is pending without changing anything, and npm approve-scripts --all to approve everything currently pending.
The migration itself is four steps and it is genuinely quick:
- Run a normal install so npm builds the tree and records what it skipped.
- List what is pending and read it. This is the step people will skip and it is the entire value of the exercise.
- Approve what you actually need, package by package, then rebuild.
- Commit the resulting
package.jsonchange, and set strict mode in CI so an unapproved script fails the build instead of shipping a broken artifact.
That last one is the important one. The point of strict mode in CI is not paranoia; it is converting the silent runtime failure described above into a loud build failure, which is where you want it.
The bit that will decide whether any of this works
The most serious criticism of this change is not technical, and it came up immediately in the community discussion, which ran to several hundred comments. One analyst’s phrasing, quoted by InfoQ, is worth reproducing: repeated broken builds “will turn deny by default into a click-through prompt.”
That is exactly right, and it is the same failure that made browser certificate warnings and mobile permission dialogs useless. If approving scripts becomes a reflex that developers perform to make a red build go green, npm will have moved the attack from “runs automatically” to “runs after somebody clicks yes without reading,” which is a smaller improvement than it looks.
The thing that prevents this is boring and procedural: the allowlist lives in package.json, which means an addition to it is a diff, which means it shows up in code review. A new entry in that list should get the same attention as a new dependency, because that is precisely what it is. A team that approves scripts in the terminal and commits the result without comment has bought very little. A team where “why does this package need to run code at install time?” is a normal review question has bought most of the 53 percent.
The other half of the announcement: publishing tokens
Buried in the same release is a change that matters to anyone who publishes packages rather than only consuming them, and it has a deadline.
npm is retiring granular access tokens configured to bypass two-factor authentication, in two phases:
- Early August 2026 (already in effect): 2FA-bypass tokens can no longer perform sensitive account, package and organisation actions. Creating or deleting tokens, changing a password, email or 2FA settings, modifying package access, managing org membership. All now require a human.
- Around January 2027: those tokens lose the ability to publish directly. What remains is reading private packages and staging a publish that a human then approves with 2FA.
The recommended replacement is trusted publishing via OIDC, where your CI system exchanges its own identity for a short-lived credential scoped to one job, instead of holding a permanent key in a settings file. If you publish anything to npm from a pipeline, this is the change to schedule before the holidays rather than in January.
When this reaches you whether you act or not
npm 12 is the current release, so anyone installing npm directly has it. The bundled-with-Node story is more interesting.
Node.js ships a specific npm version with each major. The npm team has said it hopes to backport v12 to Node 24 and Node 26, though that decision belongs to the Node release team. If the backport lands, these defaults arrive on both active LTS lines on the next patch most teams take, which for a lot of projects means arriving without anyone deciding to adopt it.
If it does not land, the wait is longer than it used to be. Under the annual release schedule Node adopted this year, there is one major per year landing each April, which puts Node 27 in April 2027.
Either way, our recommendation is the same as it was for the Astro 6 upgrade: do it on a Tuesday morning of your choosing rather than on the morning it does it to you. Move to npm 11.16.0 or later now, read the warnings, and you will have already done the migration when the default flips underneath you.
If you are the one paying for the software
You do not need any of the above. You need to send four sentences to whoever maintains your project, and you need to recognise a good answer.
- Are we on npm 12, or on npm 11.16 or later with the new warnings visible? Anything older and this has not started.
- Is there an approved-scripts list committed to the repository, and does adding to it go through code review? The list matters less than the review.
- Does CI fail the build when an unapproved script is skipped, or does it warn and carry on? It should fail. A warning here becomes a runtime crash in production.
- If we publish anything to npm, are we still using a token that bypasses 2FA? If yes, that stops working for publishing around January 2027, and it should be replaced with trusted publishing before then.
A competent developer answers each of these in a sentence. As we said last time, a blank stare is itself information.
The bottom line
For once, the industry did the thing that actually helps rather than the thing that sells. Turning off automatic code execution at install time is not clever, it is not a product, and nobody gets to put it on a slide. It just removes the mechanism that roughly half of npm’s malicious packages relied on.
The risk now is entirely in how it is adopted. Done properly, with a reviewed allowlist and a strict CI gate, it closes the biggest hole in the JavaScript supply chain. Done as a reflex to make builds green again, it becomes another dialog people click through.
That is not a technical decision. It is a habit, and habits are set in the first month.
If you want a straight answer about whether your own project is affected, or you would like the migration done for you before the default arrives on its own, send us a note. On a typical small project this is under an hour of work, and the pending-scripts list is worth reading even if you change nothing.
Sources
- npm install-time security and GAT bypass2fa deprecation, GitHub Changelog, July 8, 2026, the primary announcement of all three default changes and the token deprecation phases
- npm CLI changelog, v12, npm Docs, for the release date and the verbatim wording on
allowScriptsand the approval commands - Preparing for npm v12: install scripts and non-registry sources become opt-in, the npm team’s migration discussion, source of the CI guidance, the
allow-gitenum values, theignore-scriptsprecedence trap and the affected-package categories - npm 12 released: install scripts off by default as registry moves to explicit trust, InfoQ, for the JFrog 53 percent figure, the pnpm, Yarn and Bun comparison, the Node 24 and 26 backport position, and the approval-fatigue criticism quoted above
- npm v12 ships with install scripts off by default, Socket, for the list of packages that break and the
strict-allow-scriptsbehaviour - npm 12 disables install scripts by default to reduce supply chain risk, The Hacker News, July 2026