# NPM 12 Overhauls Script Execution Security: Default Block for Dependency Code in July Release


GitHub's npm package manager is implementing a fundamental security overhaul to combat escalating supply chain attacks. Starting with NPM version 12, expected in July 2026, dependency scripts will no longer execute automatically during installation—a critical shift designed to close a major attack vector exploited by recent high-profile malware campaigns, including the Shai-Hulud self-replicating worm and TeamPCP attacks that have compromised thousands of developers.


The change marks the most significant security architecture revision in npm's history, reflecting the growing sophistication of adversaries targeting the JavaScript ecosystem through malicious package initialization code.


## The Threat: Supply Chain Attacks via Automatic Script Execution


The npm ecosystem has become a prime target for attackers seeking to distribute malware at scale. Over the past several months, sophisticated threat actors have weaponized npm's default behavior—automatically executing scripts during package installation—to inject malware directly into developers' machines and, by extension, into deployed applications.


Recent attacks exploited for this change:


  • Shai-Hulud Miasma attacks relied on weaponized binding.gyp files—configuration files used for building native Node.js modules—to execute arbitrary code during installation
  • TeamPCP malware leveraged the same automatic script execution mechanism to infect developer environments
  • Megalodon supply chain attack compromised over 5,500 GitHub repositories
  • Red Hat NPM incident affected 32 packages published under Red Hat's namespace

  • The common thread: all these attacks succeeded because developers had no control over whether installation scripts actually ran. The moment a developer executed npm install, the attacker's code executed with the same privileges as the developer themselves.


    ## Background and Context: The Evolution of npm Security


    npm's script execution feature was designed for convenience. When developers install packages, many require compilation or setup—building native bindings, generating configuration files, or running initialization routines. Allowing these scripts to run automatically streamlined the developer experience.


    However, this convenience created a trust assumption that didn't reflect modern threat reality. In the open-source ecosystem, transitive dependencies—packages that your dependencies rely on—are largely beyond your visibility and control. Installing a popular package means trusting not just that package's authors, but hundreds of indirect dependencies, any of which could be compromised or malicious.


    Attackers have capitalized on this asymmetry. By compromising a moderately popular package or publishing convincing typosquatted packages, they gain code execution on thousands of developer machines simultaneously. The npm registry, despite security improvements, remains a high-value target precisely because it grants automatic execution privileges.


    GitHub's decision to change the default reflects a security principle gaining traction across the industry: deny by default, allow explicitly. This is the same principle that governs capabilities in operating systems, firewalls, and permission models. If code execution is not essential to package installation, it should require explicit permission.


    ## Technical Details: How the New Security Model Works


    NPM 12's changes affect multiple execution pathways:


    ### Scripts Blocked by Default


    Starting in July, these script types will no longer execute automatically:


    | Script Type | Impact | Workaround |

    |---|---|---|

    | preinstall | Runs before package installation | Must be explicitly approved |

    | install | Runs during installation | Must be explicitly approved |

    | postinstall | Runs after installation | Must be explicitly approved |

    | prepare | Runs on git/file/link dependencies | Must be explicitly approved |

    | Native node-gyp builds | Compiles C/C++ extensions | Must be explicitly approved |


    ### The Approval Workflow


    GitHub provides a structured migration path using the npm approve-scripts --allow-scripts-pending command. This tool:


    1. Scans your project's dependencies for scripts

    2. Identifies which packages attempt to run code

    3. Generates an allowlist that developers explicitly review

    4. Writes approved packages to package.json for version control


    Migration steps:


    npm approve-scripts --allow-scripts-pending
    # Review the list of packages requesting script execution
    # Approve only packages you recognize and trust
    # Commit updated package.json

    Developers using NPM 11.16.0 or later already receive warnings when installation scripts execute, allowing time to audit dependencies before the mandatory July cutoff.


    ### Git and Remote Dependencies


    The scope extends beyond local scripts:


  • Git dependencies will no longer resolve automatically, preventing attacks where a malicious .npmrc file overrides the git executable
  • Remote URLs and HTTPS tarballs require explicit permission via the --allow-remote flag (available since NPM 11.15.0)

  • This closes additional code-execution pathways that earlier versions left open.


    ## Implications for Developers and Organizations


    This change touches virtually every JavaScript developer and organization relying on npm. The implications are mixed—security gains balanced against migration friction.


    Benefits:


  • Reduced attack surface: Blocking automatic code execution eliminates a major supply chain attack vector
  • Explicit trust model: Developers explicitly approve scripts rather than implicitly trusting transitive dependencies
  • Audit trail: The allowlist in package.json provides visibility into which dependencies run code
  • Better than the alternative: Without this change, supply chain attacks will only proliferate

  • Challenges:


  • Migration effort: Large projects with hundreds of dependencies may require manual review of dozens of scripts
  • Dependency fragility: Some packages legitimately need native builds; blocking them could break installation
  • Ecosystem adjustment: Package authors must communicate their scripts' necessity; some packages may see adoption drop if users disable their scripts
  • Build pipelines: CI/CD systems relying on automatic script execution will require configuration updates

  • ## Recommendations for Developers


    ### Immediate Actions (Now Through June)


    1. Upgrade to NPM 11.16.0+ to receive warnings about scripts in your current projects

    2. Audit your dependencies: Run npm approve-scripts --allow-scripts-pending and carefully review the output. Only approve packages you recognize and trust.

    3. Update documentation: Record which packages require scripts and why in your project README or internal wiki

    4. Test on staging: Deploy the allowlist to staging environments first; verify builds and functionality remain intact


    ### Longer-Term Strategy


  • Minimize script dependencies: Evaluate whether packages requiring native compilation are necessary, or whether JavaScript-only alternatives exist
  • Monitor package updates: When upgrading dependencies, re-run the approval process to catch new scripts
  • Participate in ecosystem discussions: Engage with package maintainers if their scripts are legitimately necessary; community feedback shapes library design
  • Keep npm updated: Plan upgrades to NPM 12 as part of your regular maintenance cycle, not as an emergency scramble

  • ### For Security Teams


  • Supply chain policy: Update your software supply chain security policy to reflect the new approval model; consider mandating explicit allowlists in package.json
  • Scanning: Integrate script scanning into security scanning pipelines; tools can validate that allowlists contain only expected packages
  • Training: Educate developers about why this change exists and how to handle the approval workflow

  • ## HackWire Analysis


    NPM's gamble on usability versus security reveals a critical truth about the open-source ecosystem: the current trust model is broken. For years, developers have installed packages by running npm install—a command that sounds passive but actually executes arbitrary code from hundreds of indirect dependencies. This works until it doesn't, and when it fails, it fails at scale. The Shai-Hulud attacks and TeamPCP malware proved that attacks exploiting this vector are not theoretical; they're operational, effective, and profitable.


    The timing matters. These policy changes don't happen overnight. GitHub is telegraphing a six-month warning period and providing explicit tooling (npm approve-scripts) specifically because they understand the migration cost. The company is betting that the pain of reviewing dependency scripts now is smaller than the pain of widespread supply chain compromises later. History suggests they're right.


    But here's the nuance other coverage misses: this doesn't solve the problem; it shifts the burden. Developers who never looked at their dependency tree before now have to evaluate dozens of packages and decide whether to trust their scripts. For most developers, that decision will be reflexive—"is this a well-known package?" rather than "does this script actually need to run?"—which preserves much of the original risk. A sophisticated attacker could still compromise a legitimate popular package and hide malicious code in an installer script; the difference is that developers *could* catch it if they cared to review. That's not nothing, but it's also not a complete solution.


    The real lesson is that JavaScript's dependency model is structurally risky. The node_modules directory can contain thousands of packages, many of which you didn't explicitly request. Until the ecosystem moves toward *stronger verification* (cryptographic signatures, reproducible builds, binary provenance) or *reduced transitive dependencies* (better packaging practices), supply chain attacks will remain inevitable. NPM 12 is a necessary guardrail, but it's not an endpoint.


    For defenders: Expect a 2-3 month surge in tickets from developers asking, "Why isn't my build working?" The answer will almost always be, "That package needs script execution; add it to your allowlist." For organizations with strict security policies, now is the time to decide: do we allow script execution at all, or do we vet packages that require it through a formal process? The allowlist approach makes that conversation possible for the first time.


    — HackWire Editorial


    ---


    ## Related Coverage


  • Read more in our [Vulnerabilities](https://www.hackwire.news/category/vulnerabilities) coverage
  • Cross-reference with [Breaches](https://www.hackwire.news/category/breaches) and [Malware](https://www.hackwire.news/category/malware)
  • Stay current via the [HackWire homepage](https://www.hackwire.news/)