Post-Mortem: NPM Is Compromised

· 5 min read

The Day $0.05 Made Everyone Panic

Here’s what makes this npm mess both hilarious and terrifying: the attackers compromised packages with over 2 billion weekly downloads, sent every JavaScript developer into crisis mode, and walked away with about five cents worth of crypto.

The irony? Security vendors probably made more money from the panic than the attackers did. But that’s why this story matters. Sometimes the biggest wake-up calls come from disasters that barely happened.

How One Email Broke JavaScript

The attack started with a simple but effective phishing campaign. A maintainer of massively popular packages like chalk and debug received a fake npm support email from a lookalike domain (npmjs.help). The phishing site captured credentials, granting attackers access to publish malicious versions.

fake npm support email from a lookalike domain (`npmjs.help`)

The poisoned packages included 18 popular libraries. Downloads for these libraries exceeded 2 billion weekly. The malicious versions injected code that hooked into browser APIs (fetch, XMLHttpRequest, window.ethereum) to silently swap crypto wallet addresses.

The campaign lasted only 2.5 hours on September 8, 2025. Projects that performed a fresh install or dependency update during that window pulled in the malicious versions. Projects pinned with package-lock.json or not updating weren’t directly affected.

Where Everyone Freaked Out (And Why)

Slack channels and incident bridges lit up instantly: “Is our build system compromised?” “Which packages do we use?” “How do we even check this?”

The incident revealed a painful truth: most teams had no visibility into their dependency trees. For six frantic hours, npm ls became the most-used command in JavaScript.

Companies with strong hygiene-locked dependencies, incident response processes, and monitoring-handled the crisis. Everyone else panic-installed security scanners, hoping to find quick answers.

What Actually Went Wrong

One person, billions of installs: A single compromised maintainer account put billions of downloads at risk. That concentration of responsibility is a systemic issue in open source.

Dependency opacity: Few developers could quickly answer, “Which version of chalk are we running?” Blind trust in the registry proved naive.

Incident chaos: The technical exploit was simple. The operational failures-lack of visibility, no playbooks-turned it into an industry-wide fire drill.

The Silver Lining

Despite the chaos, the ecosystem’s response was fast:

Actual damage was minimal. The attacker netted ~$0.05. But the panic revealed both the fragility and resilience of the ecosystem.

What We Learned (The Hard Way)

The Uncomfortable Questions

This wasn’t a sophisticated nation-state attack. It was a simple phishing email. What happens when well-resourced actors try the same thing?

The JavaScript ecosystem’s strengths-speed, reuse, openness-are also its weaknesses. Every npm install is a trust relationship with a maintainer you’ve never met.

The Real Cost

This attack didn’t cost five cents. It cost trust.

CTOs are now demanding supply chain visibility. Security teams are rewriting policies. Developers are realizing npm install isn’t as innocent as it seems.

The npm ecosystem just had its “welcome to enterprise security” moment. We won’t go back to the naive optimism of blind trust in open source packages.

The attackers got their five cents. The rest of us got an expensive education in supply chain security.