Skip to content

npm Install Silently Ran Any Code for 16 Years. That Ended July 8.

DS
LDS Team
Let's Data Science
8 min
npm v12 shipped July 8, 2026, and reversed one of software's oldest defaults: dependencies no longer get to run install scripts, pull from Git, or fetch remote tarballs unless a developer explicitly allows it. The change follows North Korea-linked attacks that hijacked the Axios and Mastra AI packages through exactly that mechanism, and it is already breaking builds that never opted in.

Type npm install and, until earlier this month, you were also signing a blank check. Every package in your dependency tree, including the ones four or five levels down that you never chose and have almost certainly never read, could run shell commands on your machine the instant the install finished. No prompt. No warning. Just execution, with whatever permissions the person running the install happened to have.

Most developers never thought about this. It was simply how npm install worked, from the tool's earliest days.

Attackers thought about it constantly.

On July 8, 2026, GitHub shipped npm v12 and shut that door. Tech Times and other outlets covering the release called it the most significant security redesign in the package manager's 16-year history. For the first time, running npm install does not automatically hand code-execution rights to every dependency in the tree. It asks first.

The change is already breaking builds. Teams that skipped the migration warnings are finding out why.

The Default That Just Flipped

npm v12 does not add a new security feature so much as it removes an old assumption. According to GitHub's own changelog for the release, three behaviors that used to run automatically now require explicit opt-in.

The first: install, preinstall, and postinstall scripts, the lifecycle hooks a package can define to run arbitrary code during installation, no longer execute unless the package is explicitly approved. That includes native addons. A package that ships a binding.gyp file, signaling that it needs compilation, used to trigger an automatic node-gyp rebuild even when the package declared no install script at all. npm v12 blocks that implicit build exactly as it blocks a declared one.

The second: Git dependencies, whether a project depends on them directly or picks them up several layers down, no longer resolve unless the install runs with --allow-git. GitHub's changelog notes this closes a specific workaround, in which a malicious Git dependency's own .npmrc file could override the path to the system's Git executable and achieve code execution even on projects that had already turned on the older --ignore-scripts protection.

The third: dependencies fetched from remote URLs, such as HTTPS tarballs, no longer resolve without --allow-remote. Two related flags, --allow-file and --allow-directory, are not changing.

BehaviorBefore npm v12After npm v12
Lifecycle scripts (preinstall, install, postinstall)Ran automatically for every dependencyBlocked unless approved with npm approve-scripts
Implicit native builds (packages with binding.gyp)Ran automatically, even with no declared scriptBlocked, same as any other lifecycle script
Git dependencies (direct or transitive)Resolved automaticallyBlocked unless installed with --allow-git
Remote URL and tarball dependenciesResolved automaticallyBlocked unless installed with --allow-remote

None of this arrived without warning. The --allow-git default was previously announced on February 18, 2026, and available starting in npm 11.10.0. The --allow-remote flag arrived in npm 11.15.0. Starting with npm 11.16.0, any install that would break under v12 has surfaced an advisory warning instead of silently continuing, giving teams a runway before enforcement began.

Two Attacks in Four Months

GitHub's changelog does not tie the release to any single incident by name. The timing is hard to miss regardless.

In March 2026, attackers linked to the North Korean threat group Sapphire Sleet hijacked a maintainer account for Axios, the HTTP client library that gets roughly 100 million downloads a week, and shipped malicious versions built around a postinstall hook. The fallout stretched well beyond the library itself, far enough to reach OpenAI's own macOS code-signing infrastructure.

In June, the same group compromised 144 packages inside the Mastra AI framework's npm scope in a single automated run that took roughly 88 minutes, again through a postinstall hook, according to Microsoft security researchers who later attributed the attack to North Korea.

Both incidents depended on the exact default npm v12 now turns off. Had v12's rules already been in place, neither compromised package could have run its payload in a project that had not explicitly approved it, no matter how convincing the hijacked release looked.

Axios and Mastra AI also were not isolated. They landed inside a longer run of worm-driven npm compromises through 2025 and 2026, including one that hijacked 32 of Red Hat's own npm packages by exploiting a trusted-publishing pipeline rather than a postinstall hook at all. By the time v12 shipped, npm was the last major JavaScript package manager still granting install-time execution automatically. pnpm turned it off by default in version 10. Yarn Berry and Bun already blocked lifecycle scripts too.

What to Do Before Your Build Breaks

GitHub's own migration guidance is not complicated, but it requires action before the upgrade, not after a failed deploy.

  • Upgrade to npm 11.16.0 or later and run a normal npm install. Anything v12 would block already shows up as a warning rather than a failure.
  • Run npm approve-scripts --allow-scripts-pending to generate the full list of dependencies whose scripts, Git sources, or remote tarballs would stop working under v12.
  • Review that list. Approve the packages that genuinely need install-time execution with npm approve-scripts, and block everything else explicitly with npm deny-scripts.
  • Commit the resulting allowlist in package.json. It is meant to be a versioned, reviewable artifact, not a setting re-decided on every developer's laptop.
  • If a project has a real reason to install from Git or a remote tarball, add --allow-git or --allow-remote deliberately, rather than reaching for them the first time a build fails.

Monorepos need a specific note: the allowlist is scoped per workspace, so approving a package at the repository root does not carry over to a nested package with the same dependency. Each workspace needs its own review.

Not a Silver Bullet

Not everyone believes npm v12 solves the problem it targets, and the sharpest version of that argument comes from inside the Node.js project itself.

Ulises Gascon, the Node.js AI Security Engineer in Residence and the primary contact for the OpenJS Foundation's CNA, wrote in a NodeSource post that npm v12 "is a change I have wanted for years, and on its own it is not going to save you." His point: npm v12 removes a trigger, not a capability. Running npm install never actually loads a package's own code; it downloads the package and runs its lifecycle scripts. The package's ordinary JavaScript, the part that does the real work, only runs later, the first time an application imports it, and nothing about v12 touches that moment.

Moshe Siman Tov Bustan, a security researcher at OX Security, raised a related point in comments reported by Cybernews. "Account takeover remains an open wound," he said. "Once an actor controls a legitimate maintainer's credentials, no amount of install-script blocking helps, because the malicious code ships as a trusted, signed release."

A third concern is more behavioral than technical. Paul McCarty, a vulnerability researcher who publishes as Open Source Malware, warned that the allowlist model's biggest risk is what happens when it becomes friction instead of protection. "When the choice is 'this builds' and 'this is less prone to malware,' the former will always win," he wrote, predicting that many teams will approve dependencies in bulk just to silence the warnings, recreating the old blanket-trust default in a form that is harder to audit, not easier.

The Bottom Line

For 16 years, running npm install meant trusting every package in a dependency tree to behave, with no way to say no in advance. npm v12 replaces that blank trust with a written record: a specific list of which packages get to run code at install time, checked into source control and reviewed like any other change to a codebase.

That is a genuine fix to a problem that fed a year of increasingly automated supply chain attacks. It is not the end of the problem. A compromised maintainer account can still ship malicious code that runs the moment an application imports it, no install script required, and npm v12's allowlist has nothing to say about that. Isaac Evans, founder and CEO of the security firm Semgrep, told Infosecurity Magazine he expects attackers to respond by pushing up a layer, toward private registries like Artifactory and Nexus that npm's defaults never covered to begin with.

The tool got harder to weaponize through installation. Whether that turns out to be enough depends on how many teams actually read the list of packages they just approved, rather than clicking through it to make the warnings stop.

Sources

Practice interview problems based on real data

1,625 SQL & Python problems across 15 industry datasets — the exact type of data you work with.

Try 250 free problems