BLOG
The Pros and Cons of NPM v12's Security Improvements
NPM package manager (v12) closes serious security gaps, including making install scripts opt-in, but implementing it may not have the intended outcomes.
By c0a15726-c5b1-4b0d-85e6-fe15553df9e2 ·
On June 9, NPM announced the breaking changes coming in v12, and I'm genuinely excited about this announcement! Three permissive defaults that have shipped malware to developers for a decade are about to flip to deny-by-default. Pair that with install-time cooldowns landing across every package manager and the rise of commercial supply-chain firewalls, and you'd be forgiven for thinking the npm malware problem is finally getting solved. (It's not! Don't be fooled.)
A year ago I stood on a DEF CON stage and walked through why NPM install scripts are terrible. So let me be the first to say it: these changes are good. They are also, in part, theatre — and understanding which part is which is important.
What's actually changing in NPM v12?
NPM v12 flips three dangerous defaults:
The big one is install scripts. Today,
npm installhappily runspreinstall,install, andpostinstallhooks from any package in your tree, including transitive dependencies you've never heard of. In v12, that stops by default — no automatic lifecycle scripts, no nativenode-gypbuilds, nopreparescripts from git, file, or link dependencies. You opt packages back in withnpm approve-scripts, discover what's affected withnpm approve-scripts --allow-scripts-pending, and block the rest withnpm deny-scripts.The second change makes git dependencies require an explicit
--allow-git, closing a code-execution hole where a git dependency's own.npmrccould override thegitexecutable — a bypass that worked even with `--ignore-scripts` set.The third makes remote URL (HTTPS tarball) dependencies require
--allow-remote.
It ships around July 2026 and you can prepare today in NPM 11.16.0+.
I don't want to be cynical about the right things. Killing automatic install scripts is the single most important change NPM could make. Install hooks are the mechanism behind a huge share of the incidents we document at OpenSourceMalware — they're how a poisoned transitive dependency gets to run arbitrary code on a developer laptop or a CI runner the instant it's pulled. The security community and pnpm have been arguing for deny-by-default here for years. NPM finally agreeing is a genuinely good day.
First observation: off-by-default only works if it stays off
Here's the thing the announcement glosses over: Disabling install scripts is going to break a lot of stuff. An enormous amount of legitimate software gets installed, built, and wired into your application environments via install scripts. There's a simplistic narrative going around that lifecycle scripts only exist to do bad things, and that's just not true.
NPM packages are not just a way to import libraries. Many people, including me, build CLI tools to do necessary utilitarian functions, and many of those tools use install scripts. Some examples of popular packages that use lifecycle scripts are:
esbuild (200 million weekly downloads)
sharp (60 million weekly downloads)
core-js (40 million weekly downloads)
puppeteer (10 million weekly downloads)
Nx (9 million weekly downloads)
bufferutil (6 million weekly downloads)
utf-8-validate (8 million weekly downloads)
bcrypt (5 million weekly downloads)
Additionally, native modules need node-gyp to compile against your platform at install time. Tools that download a platform-specific binary, generate a config, register a shell completion, or build a native addon all lean on install scripts to do their job. This isn't an edge case — it's a meaningful slice of the most-depended-on packages in the registry. The day v12 lands, those packages stop working until someone approves them.
So what will developers actually do? They'll approve. Then they'll approve again. And by the third time a build breaks at 5pm because some transitive dependency needs its postinstall hook, the approval becomes a reflex. The deny-by-default protection quietly degrades into a click-through prompt, and a click-through prompt that fires constantly trains people to click through. We've watched this movie with browser permission dialogs and OS security prompts. There's no reason to expect NPM's to end differently. The control is real; the human standing in front of it is the same human who has a deadline.
If you really want to know if disabling install scripts will have the intended effect, you can look to the VSCode ecosystem. With VSCode before version 1.109, the global ``task.allowAutomaticTasks`` setting was enabled by default. This meant that malicious tasks files would automatically run if victims opened source code that included those tasks files. Microsoft changed disabled this feature to be disabled by default in January 2026 after months of threat actors using malicious tasks files to compromise developers.
Did that stop threat actors from continuing to use VSCode tasks? Nope. North Korean threat actors continue to use malicious VSCode tasks files as many developers have re-enabled the feature, or other developer tooling has enabled it. In fact, most of the large scale attacks we've seen in 2026 leverage VSCode tasks and settings files to help re-distribute the attack artefacts.
Second observation: Bad guys will find a way
Just because NPM won't run scripts at install time doesn't stop users from running those scripts the second those packages are installed. Even worse, if you already use a library and it's compromised, you don't need to run install scripts to receive the payload. It's going to run in your application. No scripts needed.
Now follow the incentives one step further. If install scripts are off by default, some package authors with legitimate needs will stop relying on them. Good ones will document a manual build step. Others will move the work somewhere NPM can't turn it off — a curl | bash in the install.js, a separate bootstrap binary, a setup command you run after install.
The attackers will make the exact same move, because of course they will. If the postinstall hook no longer fires automatically, you don't give up — you find the install path that still executes. This is precisely the kind of threat actor behavior I talked about at DEF CON push on one control and the malicious activity relocates to where the controls aren't. It doesn't vanish.
And here's the part that should worry NPM but won't, because it's good for them. When the malware moves out of the registry and into a shell script on someone's gist, or a binary downloaded from a CDN, NPM gets to report that registry-resident malware is down. They'll claim victory. The problem will look solved on their dashboard. Meanwhile the risk has simply relocated to terrain with less visibility and fewer tools, not more — off the one surface the entire security industry actually instruments, and onto surfaces nobody is watching. That's not a win. That's a measurement artifact.
Third observation: it gets harder for defenders, not easier
Between cool down periods and the disabling of install scripts, large scale NPM attacks will become less frequent. But, when you push legitimate functionality off the well-lit path, you don't just move it — you make it look guilty. A package author who genuinely needs to fetch a platform binary at install time, now doing it through some indirect mechanism to survive the new defaults, produces a fingerprint that looks exactly like evasion. Obfuscated loader, out-of-band fetch, install-time network call to a non-registry host. Five years ago that was a strong malware signal. After v12, a chunk of it is just legitimate software adapting to a stricter world.
So while the number and frequency of the big ATO style NPM attacks go down, the signal-to-noise ratio for everyone hunting malicious packages gets worse. The benign and the malicious converge on the same suspicious-looking pattern. We end up triaging a flood of weird-but-fine packages to find the weird-and-actually-bad ones, and the bad ones get better cover precisely because so much legitimate behavior now looks the same way. You bury the needed functionality in something that looks sketchy, and you've built the perfect place to hide a needle — a pattern that looks suss except it isn't. Until it is.
Other security improvements are taking hold in the community
Outside of npmv12, we are seeing everyone talking about cooldown periods and package firewalls.
Cooldowns are everywhere now. NPM shipped
min-release-agein 11.10.0 back in February — a setting that refuses to install a version until it has been public for a configured number of days. The logic is simple and effective: compromised releases usually get caught and pulled within hours, so a short delay filters most of them out at the install layer with zero scanning. Worth noting that NPM was last to this party. pNPM shippedminimumReleaseAgein 10.16 in September 2025, yarn addednpmMinimalAgeGatein 4.10.0 the same month, and bun followed in 1.3. There's even an open proposal to make seven days the default, which is the right instinct — almost nobody needs a package the instant it's published.Package firewalls have become a product category. Developers are finally installing something on their machines that address malicious package threats. Datadog released an open-source supply-chain-firewall in 2024, and Liran Tal released his tool npq back in 2017. Tools like these wrap package managers and check to see if the user is about to install vulnerable or malicious packages and if so, they'll block them from being installed. There has been a lot of new tooling in this domain recently with Socket, Aikido, and Endor Labs all offering products in this space. "Package Firewalls" like these work, but rely on developers to not bypass their controls to install malicious packages.
Cooldowns are the cheapest high-value control in the entire ecosystem. Firewalls and package managers give teams a real shot at stopping a supply chain attack before it lands. None of this is fake security. But they're also an admission that npm isn't stopping malware. The reason a third-party product can flag a malicious version six minutes after it's published is that npm isn't doing it themselves. The reason teams pay for an interception layer in front of the registry is that they can't trust the registry to keep malware out.
We are watching detection and response get pushed onto users and onto a handful of vendors, while the registry that profits from being the default keeps under-investing in its own scanning. The incident cadence so far in 2026 makes the gap obvious. v12 is NPM catching up to the problem set I laid out a year ago. That's progress. But a registry that has been this chronically under-resourced on internal security doesn't get to flip three defaults and call the supply-chain problem handled.
What actually moves the needle? Where does that leave us? With the unglamorous truth that tooling is necessary and nowhere near sufficient.
Turn on cooldowns today — it's the single best ratio of effort to protection available, and there's no reason to wait for v12. Prepare your approve-scripts allowlist now, on 11.16.0+, so the v12 upgrade doesn't break your builds and stampede your team into rubber-stamping everything. If you can manage package firewalls, run them. Do all of it.
But understand that the thing which actually dents your malicious-NPM risk isn't any single control. It's three things no vendor can sell you. First, knowing exactly what you depend on — every direct and transitive package, why it's there, and what it's allowed to do at install time. Second, understanding the threat well enough to tell adaptation from evasion when the two start looking alike. And third — the one nobody wants to hear — management giving development teams the actual time to do supply-chain security instead of treating it as something that happens for free between feature deadlines.
NPM flipping its defaults is good. Cooldowns are good. Firewalls are good. But none of them substitute for a team that knows its dependencies and has the time to care about them. The controls move the malware around. Only the team makes it matter less.