<p>npm v12 is now generally available, and it ships with a fundamental shift in how the Node.js package manager handles install-time code execution. After a year of devastating supply chain attacks that exploited npm's install scripts to spread worms, steal credentials, and compromise thousands of repositories, the new release turns off install scripts, git dependencies, and remote URL dependencies by default.</p>
<p>The change marks the end of an era where every npm install was a blind trust exercise. If you maintain a package, run a CI pipeline, or manage a JavaScript monorepo, here is everything you need to know about what changed, why it matters, and how to prepare.</p>
<h2>Background: The Install Script Problem</h2>
<p>npm install scripts have been the primary vector for supply chain attacks on the JavaScript ecosystem since late 2025. Worms like Miasma and Shai-Hulud weaponized the postinstall and preinstall lifecycle hooks in package.json to execute arbitrary code the moment a developer ran npm install. Worse, attackers discovered that even packages without explicit install scripts could execute code through the implicit node-gyp rebuild that npm triggers for any package containing a binding.gyp file. The Miasma "Phantom Gyp" technique exploited this exact gap, slipping past scanners that only watched preinstall and postinstall fields.</p>
<p>The scale was staggering. The Shai-Hulud campaign compromised over 100 npm and PyPI packages. The Miasma worm burrowed into 73 Microsoft GitHub repositories. North Korean threat actors backdoored 144 Mastra AI packages in 88 minutes through compromised npm accounts. By mid-2026, npm install scripts had become the single most reliable entry point for software supply chain attacks.</p>
<h2>How npm v12 Changes the Game</h2>
<p>npm v12 introduces three default-breaking changes that together close the install-time execution gap:</p>
<p><strong>1. allowScripts defaults to off.</strong> Preinstall, install, and postinstall scripts from dependencies no longer run automatically. The implicit node-gyp rebuild for packages with binding.gyp files is also blocked. The prepare script that fires for git, file, and link dependencies is blocked too.</p>
<p><strong>2. allow-git defaults to none.</strong> Git dependencies, whether direct or transitive, no longer resolve until explicitly allowed. This closes a path where a compromised git dependency could override the git executable via its .npmrc, running code even under ignore-scripts.</p>
<p><strong>3. allow-remote defaults to none.</strong> Remote URL dependencies such as HTTPS tarballs no longer resolve unless allowed.</p>
<p>The blocking mechanism is a soft skip by default. Unapproved scripts are silently skipped, npm prints a warning, and the install still succeeds. For CI environments, a strict-allow-scripts setting turns skipped scripts into hard errors, preventing builds from proceeding with unverified dependencies.</p>
<h2>Impact on Native Modules and Binary Downloaders</h2>
<p>The change has an immediate practical impact for packages that compile native modules or download platform-specific binaries at install time. Packages like sharp, better-sqlite3, bcrypt, and node-sass all trigger the implicit node-gyp rebuild and will fail to build unless explicitly approved. Binary downloaders like Cypress, Playwright, Puppeteer, and Electron also depend on install scripts to fetch platform-specific binaries.</p>
<p>For these packages, teams need to use npm approve-scripts to pre-approve specific script executions, or use the allow-scripts flag during install. The npm team recommends migrating to platform-specific optional dependencies where possible, the model popularized by esbuild, rather than relying on install scripts.</p>
<p>vlt, the package manager from former npm CLI lead Darcy Clarke, advocates for this exact approach. Clarke also has an accepted npm RFC that would make package distributions a native format, allowing authors to ship pre-built binaries without install-time downloads.</p>
<h2>Two-Factor Authentication Token Deprecation</h2>
<p>npm v12 also begins winding down granular access tokens (GATs) configured to bypass two-factor authentication. The deprecation happens in two phases:</p>
<p><strong>Phase 1 (August 2026):</strong> Tokens configured to bypass 2FA lose the ability to perform sensitive account actions. This includes creating or deleting tokens, changing passwords or email addresses, modifying 2FA settings, managing package access and maintainers, editing trusted publishing configurations, and managing organization membership. These operations will require an interactive session with 2FA.</p>
<p><strong>Phase 2 (January 2027):</strong> Direct publishing from 2FA-bypass tokens is removed. The remaining surface shrinks to reading private packages and staging publishes, where a package only goes public after human approval with 2FA.</p>
<p>GitHub recommends moving automated publishing to OIDC-based trusted publishing or staged publishing with a human approval step, rather than long-lived publish tokens.</p>
<h2>Comparison: npm vs pnpm</h2>
<table><tr><th>Feature</th><th>npm v12</th><th>pnpm v10+</th></tr></table>
<p>|---|---|---|</p>
<table><tr><th>Install scripts default</th><th>Blocked (opt-in)</th><th>Blocked (opt-in)</th></tr><tr><td>Minimum release age</td><td>Not implemented</td><td>24 hours (v11+)</td></tr><tr><td>Strict mode for CI</td><td>strict-allow-scripts</td><td>Built-in</td></tr><tr><td>Native module handling</td><td>Soft skip + approval</td><td>Similar approach</td></tr><tr><td>2FA enforcement</td><td>Phased GAT deprecation</td><td>Built-in</td></tr><tr><td>Git dependencies default</td><td>Blocked</td><td>Blocked</td></tr></table>
<p>The two package managers are converging on secure defaults, but pnpm's earlier adoption gave it a head start. The broader ecosystem benefit is that developers now have secure defaults regardless of which manager they choose.</p>
<h2>Developer Experience: Preparing for v12</h2>
<p>The most common question from teams preparing for npm v12 is what breaks. The answer depends on your dependency graph.</p>
<p>1. <strong>Run npm approve-scripts allow-scripts-pending</strong> to list which packages have pending scripts and what each one executes. This gives you a complete inventory of install-time behavior across your entire dependency tree.</p>
<p>2. <strong>Audit build pipelines.</strong> CI environments should use strict-allow-scripts to catch unapproved script executions as hard failures before they reach production.</p>
<p>3. <strong>Check .npmrc files for unknown keys.</strong> One v12 beta change that was walked back: unknown .npmrc keys now warn instead of error, reducing friction for teams with legacy configuration. If you want the stricter check, opt in with strict-npmrc.</p>
<p>4. <strong>Plan the CI migration.</strong> The npm team hopes to backport v12 to Node 24 and Node 26 (both active LTS lines), which would bring the security defaults to a much larger audience before Node 27 ships.</p>
<h2>Frequently Asked Questions</h2>
<div class="faq-item"><h3>Will my existing projects break when I upgrade to npm v12?</h3><p>Not immediately. Skipped scripts produce warnings, not errors. However, you may notice that native modules fail to compile or binary downloaders skip their fetch step. Run npm approve-scripts to identify what needs attention.</p></div>
<div class="faq-item"><h3>Can I still run install scripts if I need them?</h3><p>Yes. Use the allow-scripts flag or configure allowScripts in your .npmrc. The default is opt-in, not a hard block.</p></div>
<div class="faq-item"><h3>How is this different from ignore-scripts?</h3><p>ignore-scripts was a global opt-out that blocked everything indiscriminately. The v12 model is granular and requires explicit approval per script type, with the option to fail hard in CI.</p></div>
<div class="faq-item"><h3>Does this protect against compromised packages?</h3><p>It prevents install-time code execution from compromised packages you have not already approved. It does not prevent a trusted package from being compromised after approval. What v12 removes is the default that let an injected script run the moment a package landed in node_modules.</p></div>
<div class="faq-item"><h3>What about npm v12 on older Node versions?</h3><p>npm v12 may be backported to Node 24 and Node 26. If approved, the new defaults would reach both active LTS lines, covering far more users.</p></div>
<h2>Key Takeaways</h2>
<ul>
<li>npm v12 blocks install scripts, git dependencies, and remote URL deps by default, closing the primary supply chain attack vector of 2025-2026</li>
<li>The implicit node-gyp rebuild path is also blocked, eliminating the Miasma Phantom Gyp technique</li>
<li>2FA-bypass tokens are being deprecated in two phases through January 2027</li>
<li>CI pipelines should adopt strict-allow-scripts to catch unapproved scripts as hard failures</li>
<li>Native modules and binary downloaders need explicit approval to rebuild or download</li>
<li>The broader ecosystem is converging on secure defaults</li>
</ul>
<h2>Conclusion</h2>
<p>npm v12 does not solve every supply chain problem. Compromised maintainer accounts, valid provenance, and social engineering attacks still work. But it removes the default that let an injected script execute the moment a developer typed npm install. For a year that saw campaign after campaign exploit that same default, the change is overdue. The ecosystem is now measurably harder to worm.</p>
<hr/>
<p class="sources">Sources: <a href="https://socket.dev/blog/npm-12" target="_blank" rel="noopener">Socket.dev analysis of npm v12</a>, <a href="https://github.blog/changelog/2026-07-08-npm-install-time-security-and-gat-bypass2fa-deprecation/" target="_blank" rel="noopener">GitHub npm v12 changelog</a></p>
Automated Transmission
This entry was synthesized and populated dynamically using native API integrations.