supply-chain security npm developer-tools cicd

npm 12 makes install scripts opt-in by default

npm 12 turns lifecycle scripts, Git dependencies, and remote tarballs into explicit choices. That is a meaningful shift for JavaScript supply-chain security.

boring.tools team

boring.tools team

The team behind boring.tools.

6 min read
npm 12 makes install scripts opt-in by default

The JavaScript ecosystem just changed one of its oldest assumptions: installing a package should not automatically mean running code from that package.

In the npm 12 changelog, GitHub announced that npm v12 is generally available and tagged latest. The release turns on install-time security defaults that make several risky npm install behaviors opt-in. The Hacker News summarized the same change as npm 12 disabling install scripts by default to reduce supply-chain risk.

That may sound like a package-manager detail. It is not. Install-time execution is one of the most useful primitives for npm supply-chain attacks: it runs before developers have imported anything, often inside laptops, CI runners, release jobs, and build environments with secrets.

What changed in npm 12

As of npm v12, three behaviors that used to happen automatically now require explicit approval.

First, dependency lifecycle scripts no longer run by default. That includes preinstall, install, and postinstall, plus implicit node-gyp builds. npm now requires teams to approve trusted scripts instead of assuming every dependency can execute code during install.

Second, Git dependencies are no longer resolved unless explicitly allowed. That matters because Git dependencies can bypass some registry-level controls, provenance expectations, and review workflows teams rely on for normal package versions.

Third, dependencies fetched from remote URLs, such as HTTPS tarballs, are no longer resolved unless explicitly allowed. Remote tarballs are convenient, but they are also harder to reason about than ordinary registry packages with stable metadata.

GitHub’s migration guidance points teams to:

npm approve-scripts --allow-scripts-pending

The resulting allowlist should be committed to package.json, making install-time trust a reviewed repository decision instead of an invisible side effect of dependency resolution.

Why lifecycle scripts are such a sharp edge

Lifecycle scripts are not inherently malicious. Many packages use them for native builds, setup checks, code generation, or compatibility work.

The security problem is that they execute at a powerful moment. A malicious postinstall script does not need your application to import the package. It runs as part of installation. If that happens in CI, the script may see environment variables, package-registry tokens, cloud credentials, GitHub Actions context, cache directories, SSH agents, or source code.

That is why so many npm incidents involve install-time hooks. Attackers do not need to exploit an application at runtime when they can compromise the development or build path earlier.

Recent supply-chain stories keep pointing at the same pattern:

  • a trusted package or maintainer account is compromised,
  • a typosquat or dependency-confusion package is installed,
  • an install script runs automatically,
  • credentials are harvested before anyone opens the application.

npm 12 changes the default from “run this unless blocked” to “do not run this unless approved.” That is a healthier baseline.

This is a supply-chain policy change, not just a CLI upgrade

The important part is not only npm’s behavior. It is the policy signal.

For years, developer tooling optimized for convenience: install the package, execute whatever it needs, and make builds work. Supply-chain attacks have made that default harder to defend. Package managers now have to assume that dependency installation is an attack boundary.

That means teams should treat npm 12 adoption like a supply-chain hardening project:

  1. Upgrade in a branch or controlled CI environment.
  2. Run installs and collect the scripts that are now pending approval.
  3. Review which packages actually need install-time execution.
  4. Commit the allowlist intentionally.
  5. Re-run builds with the same policy in CI.
  6. Remove or replace dependencies whose install scripts are unnecessary or suspicious.

The goal is not to break every build. The goal is to make install-time code execution visible enough to review.

Git and remote URL dependencies deserve the same scrutiny

Install scripts are the headline, but the Git and remote URL changes are also important.

A dependency from the npm registry has a package name, version, tarball, metadata, and an ecosystem of scanners that know how to reason about it. A Git dependency or raw tarball can be much looser. It may point to a branch, a moving ref, an external host, or an artifact outside normal registry review.

That does not make every Git dependency bad. It does mean they should be intentional.

Ask the same questions you would ask about any other supply-chain input:

  • Who controls this repository or URL?
  • Is the reference immutable?
  • Can the artifact change without a pull request?
  • Does it bypass dependency monitoring?
  • Does it execute install-time code?
  • Would CI fetch it with secrets available?

npm 12’s default forces those questions to happen before resolution, not after an incident.

Publishing tokens are changing too

The npm 12 announcement also starts deprecating the most sensitive uses of 2FA-bypass granular access tokens.

GitHub says those tokens will no longer be able to perform sensitive account, package, and organization management actions after the change rolls out. That includes actions such as creating or deleting tokens, changing account or 2FA configuration, changing package access and maintainers, changing trusted publishing configuration, and managing organization/team package grants.

GitHub expects that change in early August 2026.

A second change is planned around January 2027: 2FA-bypass tokens will no longer publish directly. Their publishing surface will be reduced to reading private packages and staging a publish, where a package only becomes public after human 2FA approval.

GitHub’s recommendation is to move automated publishing toward trusted publishing with OIDC or staged publishing with a human approval step instead of long-lived publish tokens.

This is the same theme from another angle: long-lived credentials and invisible automation should not be the default for high-impact supply-chain actions.

What teams should do now

If your projects use npm, start with a small audit.

Search for packages that depend on install-time behavior:

npm install
npm approve-scripts --allow-scripts-pending

Then review the generated allowlist. Treat each approved script as executable code that joins your build chain.

For CI/CD, check whether your pipelines rely on:

  • packages with preinstall, install, or postinstall,
  • native module builds through node-gyp,
  • Git dependencies,
  • HTTPS tarball dependencies,
  • long-lived npm publish tokens,
  • 2FA-bypass granular access tokens.

For publishing, plan the migration path early. If a release job uses a long-lived token that bypasses 2FA, it should move toward OIDC-based trusted publishing or a staged publish flow with human approval before the enforcement dates arrive.

For dependency monitoring, make sure your inventory distinguishes ordinary registry packages from Git and remote URL dependencies. They do not carry the same operational risk.

Where boring.tools fits

npm 12 gives JavaScript teams a better default, but defaults are only one layer. You still need visibility into what your projects install, what your CI/CD pipeline uploads, and which dependencies change over time.

SBOMs help with that inventory. They will not decide whether a lifecycle script is safe, but they give teams a stable artifact to compare across builds, projects, and releases. Combined with stricter install-time defaults, project-scoped CI credentials, and explicit approval for risky dependency sources, they help shrink the hidden parts of the build chain.

The direction is clear: supply-chain security is moving from “scan after the fact” toward “make trust decisions explicit before code runs.” npm 12 is a good example of that shift.


boring.tools helps teams keep dependency visibility connected to real projects and CI/CD pipelines. If your build already generates CycloneDX SBOMs, you can upload them directly from GitHub Actions, GitLab CI, Forgejo Actions, or raw curl without connecting your Git provider.