npm v12 Disables Install Scripts: What It Stops and Misses

npm v12, released on July 8, 2026, no longer runs a dependency’s preinstall, install, or postinstall hooks unless your project has approved them, and it stops fetching Git and remote tarball dependencies by default. That shuts the most common route malicious packages have used to execute code on developer laptops and CI runners. It does nothing about malicious code that runs when a package is imported, built, or invoked.

What changed with npm v12 install scripts

The v12.0.0 release notes list three install-time changes that matter for security:

  • Dependency lifecycle scripts are blocked unless approved. npm only runs a dependency’s install scripts when the root project’s allowScripts policy in package.json covers that package. GitHub’s June preview confirmed this also applies to implicit node-gyp builds and to prepare hooks on dependencies installed from git, local file, or link specs.
  • allow-git defaults to none. Git dependencies, whether direct or transitive, are skipped unless you opt in with all or root.
  • allow-remote defaults to none. Dependencies that point at a tarball URL are skipped. According to the config reference, tarballs served from the same host as your configured registry still install normally.

Two details are easy to miss. Only dependency scripts are affected: your own project’s lifecycle scripts still run, and the release notes add that the root preinstall now fires before dependencies are installed. And the Git change is about more than scripts: per the June changelog, a Git dependency’s bundled .npmrc could point npm at a different git binary, and --ignore-scripts offered no protection against that.

npm 11.16.0 and later warned about the switch in advance, and The Hacker News covered the plan in June. The current release is 12.1.0, from September 21.

Why install hooks drove so many npm supply chain attacks

Install hooks fire automatically as soon as a package lands in the tree. Nobody has to require() it, and the hooks run for transitive dependencies you have never looked at. GitHub’s July 28 security post spells out the appeal: install-time scripts let malware steal credentials immediately instead of waiting for runtime.

The last year supplied plenty of evidence:

  • Nx, August 2025. Malicious releases of the Nx build system shipped a postinstall script that searched the file system for credentials and posted them to GitHub repositories named s1ngularity-repository, according to the GitHub advisory.
  • Shai-Hulud, September 2025. A self-replicating worm hit more than 500 packages, CISA reported, stealing GitHub tokens and cloud API keys and publishing infected versions under victims’ accounts. Unit 42 found the first wave ran as a post-install script; the November wave moved to pre-install so it ran earlier in the install.
  • Axios, March 2026. An attacker who had compromised the lead maintainer’s computer published two malicious axios versions (project post mortem). The releases added a dependency called plain-crypto-js, a lookalike of crypto-js that axios never imported. Its postinstall hook fetched a cross-platform remote access trojan, and the releases stayed live for roughly three hours (Datadog Security Labs).

Under v12 defaults, the axios chain stalls: nobody would have plain-crypto-js in an allowlist, so its hook would be skipped. The RFC behind the change also argued that npm was the only major JavaScript package manager still running dependency scripts by default. pnpm 10 had already made the same move. For most of npm’s history, the way to disable npm install scripts was ignore-scripts, an all-or-nothing switch that also skipped legitimate builds.

What breaks, and how to allowlist what you trust

The usual casualties are packages that compile a native addon or download a prebuilt binary during install. After each install, npm ends with a list of the packages whose scripts it skipped. You review and record decisions with the npm install-scripts command:

# list unreviewed install scripts
npm install-scripts ls

# approve, pinned to installed versions
npm install-scripts approve sharp canvas

# deny explicitly
npm install-scripts deny telemetry-pkg

# run newly approved scripts
npm rebuild

Approvals are written to package.json, so they go through code review like any other change. Each approval is pinned to the version you reviewed unless you pass --no-allow-scripts-pin. Denials are name-only and survive a later bulk approval. The result looks like this (versions are illustrative):

"allowScripts": {
  "sharp@0.34.3": true,
  "canvas@3.1.2": true,
  "telemetry-pkg": false
}

The older npm approve-scripts and npm deny-scripts commands remain as aliases. Global installs and npx have no project file, so they take --allow-scripts=name1,name2 instead; passing that flag during a project install is an error. If you need a Git or URL dependency, set allow-git or allow-remote to root in the project .npmrc, which permits only the ones your own package.json declares.

There is also dangerously-allow-all-scripts, which restores the old behavior wholesale. npm’s docs call it a migration escape hatch whose use is “strongly discouraged.”

What the new default does not stop

Blocking install hooks removes one execution path. It does not make a malicious package safe.

  • Code that runs on import. In the September 2025 chalk and debug compromise, a phished maintainer account pushed 18 packages with more than 2 billion combined weekly downloads (Aikido). The payload lived in index.js and hijacked crypto wallet activity in the browser. No install script was involved.
  • Build and developer tooling. Your build, test runner, linters, bundler plugins, and npx commands execute dependency code on every run. Nx itself is a build tool; an attacker who expects hooks to be blocked can move the same payload into code your pipeline calls anyway.
  • Typosquats you install deliberately. A lookalike package that keeps its payload in its main module or CLI entry point runs as soon as you use it.
  • Compromised maintainers of packages you already trust. Name-only approvals cover every future version, and re-approving after an upgrade rewrites the pin to the new version. A reflexive approve --all turns the allowlist into a rubber stamp.
  • Machines still on older npm. Stale CI images and pinned toolchains running npm 11 or earlier keep executing every dependency script.

What to do now

Engineering teams

  1. Upgrade npm on developer machines and CI images, and check npm --version in the pipeline so an old image cannot slip through.
  2. Run npm install-scripts ls, read each flagged script, approve only the packages that need theirs, and deny the rest. Commit the result.
  3. Install in CI with npm ci from a committed lockfile and set strict-allow-scripts=true in the project .npmrc. That turns the default skip-and-warn behavior into a failed install, so new script-bearing dependencies surface in review.
  4. Add a cooldown. min-release-age, available since npm 11.10.0, refuses versions newer than a set number of days, and min-release-age-exclude lets urgent fixes through. GitHub now makes Dependabot wait three days before proposing version updates for the same reason.
  5. Run npm install-scripts prune periodically to remove stale entries.

Security teams

  1. Treat changes to allowScripts, .npmrc, and lockfiles as security-relevant diffs that need an owner’s review. Flag dangerously-allow-all-scripts or allow-git=all wherever they appear.
  2. Check provenance. npm audit signatures reports verified registry signatures and attestations for installed packages (npm docs). For packages you publish, adopt trusted publishing, which generates provenance automatically, and retire 2FA-bypass tokens: GitHub’s July changelog says they lose direct publish rights around January 2027.
  3. Monitor behavior, not only manifests. Alert when node or npm processes on endpoints or CI runners spawn shells, curl, or wget, read credential files such as ~/.npmrc or cloud configs, or reach domains they have never contacted. Watch for unexpected repository creation under developer accounts, which is how the Nx payload exfiltrated data.
  4. Keep a credential rotation runbook ready. CISA’s Shai-Hulud guidance included rotating all developer credentials and enforcing phishing-resistant MFA.

Monitoring is where install-time controls hand off to detection: code that gets past an allowlist usually still does something observable. AgentSOC, Jutsu’s AI-native SOC platform, ingests alerts from sources such as Wazuh, Google Workspace, and syslog, uses AI agents to enrich and triage them, correlates related alerts into incidents, and escalates uncertain cases to analysts. For background, see what an AI SOC is and our look at how trusted systems become the attack surface.

Subscribe to our newsletter

Get the latest security tips, product updates, and news delivered to your inbox.