A Fake 2FA Reset Email Just Compromised an npm Maintainer. About 500 Packages Went Bad Before Anyone Noticed.
On September 15, an npm package maintainer got an email that looked exactly like npm's own two-factor authentication reset flow. It wasn't. It came from a lookalike domain built to mimic npm's real one, and it walked the maintainer through entering their username, their password, and a live time-based one-time code, handing all three straight to an attacker in real time. By September 16, researchers at Socket had identified close to 500 downstream packages carrying malicious code published from that single compromised account.
The mechanics are almost boring, which is the point
There was no zero-day here. No clever exploit in npm's infrastructure, no cryptographic weakness, nothing that required real technical sophistication to pull off. The attacker registered a domain designed to look official, sent an email that mimicked a routine security prompt almost everyone has clicked through without reading closely, and collected credentials the old-fashioned way. The only "innovation" was harvesting the live TOTP code in the same flow, which sidesteps the entire point of having two-factor authentication in the first place: if you give the attacker your second factor at the moment they ask for it, it isn't actually a second factor anymore.
Once the attacker had account access, the rest followed a pattern that's become familiar over the past year. With publish rights to the maintainer's packages, they pushed malicious versions that got distributed automatically to anyone whose build ran npm install or npm update against an unpinned version range. The payloads reportedly included credential and cloud-token theft and traffic manipulation designed to redirect or intercept network requests, and researchers have linked at least some of the activity to the same self-replicating worm family, known as Shai-Hulud, behind a prior major npm incident.
This is not a one-off
I want to be careful with how I frame the scale here, because "500 packages" sounds dramatic and the exact count moved as researchers kept scanning in the days after disclosure, so treat that figure as a snapshot from early in the investigation rather than a final tally. What I'm more confident asserting is the pattern: this is at least the third or fourth significant npm supply-chain compromise in roughly a year, following incidents involving packages like debug, chalk, and the keyv/cacheable family. Different maintainers, different entry points, same underlying dynamic: a single compromised account with publish rights becomes a distribution mechanism for malware at the scale of however many projects transitively depend on that package.
If that sentence made you wince because you have no idea how many packages transitively depend on your dependencies, you're not alone. That's the actual problem. Most of us install a handful of direct dependencies and never look at the tree those pull in underneath them.
What a solo dev should actually change
"Be more careful about phishing emails" is true and also useless as advice, because the whole point of a well-built phishing email is that it doesn't look like one in the moment. The maintainer who got hit here wasn't careless, they got a very good fake. Vigilance isn't a defense you can rely on at scale, so the fix has to be structural instead.
A few things that actually move the needle for a solo operator's dependency risk: pin exact versions in your lockfile rather than trusting semver ranges to auto-update you into whatever gets published next, and treat that lockfile as something you commit and review, not just a build artifact. Run npm audit as a baseline, but don't mistake it for a real defense, it only catches vulnerabilities that have already been disclosed and cataloged, not a fresh supply-chain compromise from three days ago. Keep your dependency footprint as small as you reasonably can; every package you add is also every package's maintainer's account you're implicitly trusting. And when you do bump a dependency, especially one with broad reach like a logging or utility library, actually look at the diff before merging, not because you'll catch obfuscated malware by eye every time, but because an unexpected new dependency or a version jump with no matching changelog entry is a real signal worth five minutes of attention.
None of this is glamorous. It's also the only category of fix that doesn't depend on you personally outsmarting a phishing email that was specifically designed to be convincing.
The honest take
I don't think this is a story about npm being uniquely broken, PyPI and other registries have had comparable incidents, and the underlying problem, that a package registry's security model still rests heavily on individual maintainers not getting phished, is structural to how open source package distribution works everywhere, not a flaw specific to JavaScript. Where I'd push back on my own framing above: lockfile pinning and smaller dependency trees reduce your exposure, they don't eliminate it, a pinned version can still be the malicious one if you pin it after the compromise and before detection. There's no fully solved version of this problem right now, just better and worse odds.
Given that, my actual recommendation is to accept the odds framing rather than look for a silver bullet: minimize your surface area, review your lockfile diffs like you'd review a colleague's pull request, and don't let "npm audit passed" convince you the tree is clean. It wasn't clean for the projects pulling in these packages on September 15 either, and their audit would have looked fine the day before.
Author
Lukas
@lukcombinator