TL;DR

On March 21, 2026, Microsoft Threat Intelligence published a technique profile on malicious npm lifecycle scripts — the mechanism behind multiple high-profile supply chain attacks including Shai-Hulud 2.0, Contagious Interview (North Korea), and campaigns by Jade Sleet, Moonstone Sleet, and Sapphire Sleet.

The core problem: when you run npm install, any package in your dependency tree can execute arbitrary code with your user’s full privileges — filesystem, environment variables, network, SSH keys, cloud credentials. No sandbox. No approval prompt. No signature verification.

This isn’t a vulnerability to patch. It’s intended functionality being weaponized.


Why Lifecycle Scripts Are Dangerous

npm lifecycle scripts are hooks defined in package.json that fire automatically during package operations. The execution order during install:

preinstall → install → postinstall → prepublish → prepare

Every package in the dependency tree — including transitive dependencies you never explicitly installed — gets to run these hooks. The installing user’s full privileges are inherited.

Here’s what makes this particularly nasty:

  • No sandboxing: Scripts get full access to the filesystem, environment variables, network, and child processes
  • Transitive execution: A malicious package buried five levels deep in your dependency tree still fires its postinstall
  • Silent execution: Unless you’re watching terminal output carefully, you won’t notice the extra script execution
  • Legitimate by design: Malicious scripts use the same binaries and exhibit the same behaviors as legitimate build scripts, making detection a false-positive minefield

What’s Accessible

ResourceExamples
Credential files.npmrc, .git-credentials, ~/.ssh/*, ~/.azure/, ~/.aws/credentials
Environment tokensNPM_TOKEN, GITHUB_TOKEN, AZURE_CLIENT_SECRET, AWS_ACCESS_KEY_ID
NetworkHTTP/HTTPS, DNS, WebSockets
Child processesPowerShell, bash, curl, interpreters

A single postinstall script can read your .npmrc, steal your npm publish token, exfiltrate it over HTTPS, and you’d never know unless you were specifically monitoring for it.


The Attack Patterns

Malicious lifecycle scripts enable several capabilities that execute immediately on install:

Direct execution — Malicious postinstall runs inline JavaScript or spawns a child process. Simple, fast, effective.

Staged payloads — The install script fetches, decodes, and runs a remote payload. This separates the trigger from the actual malware, evading static analysis. The package.json looks clean; the real payload lives on an attacker-controlled server.

Credential theft — Scripts read .npmrc, .git-credentials, ~/.ssh/*, or enumerate process.env. Credentials get exfiltrated over HTTP to attacker infrastructure. This is the bread and butter of npm supply chain attacks.

Worm-style propagation — Stolen npm tokens are used to republish trojanized versions of the victim’s own packages. This is exactly what Shai-Hulud 2.0 did — automated pipelines accelerated the spread from one compromised maintainer to their entire package ecosystem.

How Attackers Get In

The initial compromise typically happens through:

  1. Maintainer account takeover — password compromise, phishing, or expired email domain registration
  2. CI/CD workflow compromise — injecting malicious steps into build pipelines
  3. Typosquatting — lodassh instead of lodash
  4. Dependency confusion — publishing a public package matching an internal package name

North Korea’s Sapphire Sleet has been observed registering typosquatted npm domains specifically to phish maintainer credentials.


Real-World Campaigns

These aren’t theoretical attacks. Multiple threat actors have operationalized this technique:

Shai-Hulud / S1ngularity (Aug–Nov 2025)

The Shai-Hulud campaigns used malicious postinstall scripts to steal npm tokens and credentials. In later waves, stolen npm tokens were used to inject the same payload into other packages the victims maintained — creating a self-replicating worm effect across the npm ecosystem. JFrog tracked two distinct waves of this campaign.

Contagious Interview (Oct 2025)

Tracked by Socket, this campaign deployed postinstall scripts in typosquatted packages that executed in-memory loaders. The loaders stole credentials and cryptocurrency wallets. Over 338 malicious packages were published with more than 50,000 downloads.

Moonstone Sleet (Jan 2024+)

This North Korean actor targeted individuals looking for work with a coding skills test. The test project contained malicious npm packages — when victims ran npm install to set up the project, the lifecycle hooks delivered malware.

Jade Sleet (Jul 2023)

GitHub reported with high confidence that Jade Sleet used social engineering to convince cryptocurrency developers to clone attacker-controlled repositories. When victims ran npm install, malicious preinstall hooks downloaded and executed additional malware.


MITRE ATT&CK Mapping

TacticTechniqueHow It Applies
Initial AccessT1195.002 — Supply Chain CompromiseCompromised registries, maintainer accounts, or CI/CD inject malicious lifecycle scripts
Initial AccessT1199 — Trusted RelationshipExploiting implicit trust in third-party packages
ExecutionT1204 — User ExecutionDevelopers lured into running npm install on attacker-controlled repos
ExecutionT1059.007 — JavaScriptLifecycle scripts run JS through Node.js during install
ExecutionT1059.001 — PowerShellOn Windows, scripts often spawn PowerShell for staged payloads
Defense EvasionT1027 — ObfuscationBase64 encoding, string concatenation, encrypted payloads
Credential AccessT1552.001 — Credentials in FilesReading .npmrc, .git-credentials, enumerating process.env
Credential AccessT1552.004 — Private KeysReading ~/.ssh/* for lateral movement
C2T1105 — Ingress Tool TransferStaged payloads fetched from attacker servers during install
ExfiltrationT1041 — Exfil Over C2Credentials POSTed to attacker infrastructure
ExfiltrationT1567 — Exfil Over Web ServiceCredentials sent to Discord webhooks, Slack, Telegram, Pastebin

Detection

Detection is hard because lifecycle scripts are intended functionality, not a bug. Malicious scripts look identical to legitimate build scripts. That said, there are signals worth monitoring.

Microsoft Defender Signatures

Microsoft Defender Antivirus detects known malicious npm payloads:

  • Trojan:JS/S1ngularity
  • Trojan:JS/ShaiWorm

Behavioral detections (may trigger on legitimate activity too):

  • Behavior:Win32/NodeJS.A
  • Behavior:Win32/NodeNetConnect.A

Defender for Endpoint Alerts

High-confidence alerts:

  • Suspicious Node.js process behavior
  • Suspicious NPM package retrieval

Lower-confidence (may be unrelated activity):

  • Jade Sleet threat actor activity detected
  • Moonstone Sleet actor activity
  • Possible supply chain attack activity
  • Script execution via workflow runner
  • Suspicious script launched

What to Hunt For

In your CI/CD logs and endpoint telemetry, look for:

  • node or npm processes spawning unexpected child processes (curl, wget, PowerShell)
  • Outbound HTTP POST requests during npm install to non-registry domains
  • File reads of .npmrc, .git-credentials, or ~/.ssh/ during package installation
  • Environment variable enumeration (process.env dumps) in install contexts
  • Packages with postinstall scripts that fetch remote payloads

Defensive Strategy

The only client-side control that completely prevents lifecycle script execution is --ignore-scripts. Everything else limits impact after execution has already occurred.

CI/CD Pipeline Hardening

This is your highest-leverage control.

1
2
3
4
5
# Enforce in all CI/CD pipelines
npm ci --ignore-scripts

# Or configure globally in .npmrc
ignore-scripts=true

For packages that legitimately need lifecycle scripts (native modules like node-sass, sharp), maintain an explicit allowlist.

pnpm 10+ blocks lifecycle scripts by default unless explicitly allowed. Bun also does not execute lifecycle scripts for dependencies by default. If you’re evaluating package managers, this is a meaningful security differentiator.

Dependency Hygiene

  • Run npm audit regularly and fix with npm audit fix
  • Prevent dependency confusion: use scoped package names (@yourorg/package-name) routed to your private feed
  • Pin dependencies to exact versions in production
  • Review postinstall scripts before adding new dependencies

Development Environment Isolation

  • Use containerized dev environments (GitHub Codespaces, VS Code Dev Containers) for high-risk teams
  • Distribute secure .npmrc defaults through dev container config
  • Run npm install in container jobs with read-only volume mounts

Identity & Access Controls

  • Deploy Conditional Access with Identity Protection for developer identities
  • Use step-up authentication on npm publish operations
  • Configure just-in-time access (PIM) for package publishing permissions
  • Enable npm Trusted Publishing with provenance attestations

For Organizations Publishing npm Packages

  • Enable npm provenance attestations — this lets consumers verify where a package was built
  • Use GitHub environments with protection rules for publishing workflows
  • Require signed commits through organizational rulesets
  • Protect .github/workflows/**, package.json, and package-lock.json with push rulesets and CODEOWNERS
  • Enable secret scanning with push protection organization-wide

If You Get Hit

Assume credential exfiltration has occurred. Immediate response:

  1. Rotate everything — npm tokens, GitHub tokens, service connection credentials, SSH keys, cloud credentials
  2. Audit publishes — check if your npm packages were republished with malicious payloads
  3. Check downstream — notify consumers if your packages were compromised
  4. Review audit logs — GitHub audit logs, Azure DevOps audit logs, npm access logs
  5. Deploy monitoring — stream audit logs to your SIEM for analytic rules and hunting queries

The Uncomfortable Truth

npm lifecycle scripts are not a vulnerability. They’re a feature. You can’t file a CVE against postinstall. There’s no patch coming.

The npm ecosystem has fundamentally chosen convenience over security: any package, at any depth in your dependency tree, can run arbitrary code with your credentials during installation. The only complete mitigation is --ignore-scripts, which breaks legitimate packages that need native compilation.

pnpm 10 and Bun have made the right call by defaulting to blocking lifecycle scripts. npm and yarn still execute them by default. Until that changes, every npm install is an implicit trust decision across your entire dependency graph.

The supply chain attacks aren’t stopping. Shai-Hulud demonstrated self-replicating worm behavior through npm. North Korean threat actors (Jade Sleet, Moonstone Sleet, Sapphire Sleet) are actively targeting developers through typosquatting, fake job interviews, and social engineering.

Lock down your CI/CD pipelines with --ignore-scripts. Isolate your dev environments. Monitor for credential exfiltration during installs. And audit your dependency tree — because somewhere in those 1,847 transitive dependencies, there might be a postinstall script waiting to run.


References

  1. Microsoft Threat Intelligence — Technique Profile: Malicious npm lifecycle scripts (March 21, 2026)
  2. Socket — North Korea’s Contagious Interview Campaign Escalates: 338 Malicious npm Packages
  3. JFrog — Shai-Hulud: The Second Coming
  4. GitHub Security Blog — Security alert: social engineering campaign targets technology industry employees
  5. npm Documentation — Scripts
  6. OWASP — NPM Security Cheat Sheet