One Maintainer, Two Billion Installs

On 8 September 2025 a maintainer received an email about resetting two-factor authentication. It came from npmjs.help – not npmjs.com, a domain registered to look like it at a glance.

They entered a username, a password, and a live TOTP code. The attacker used all three inside the code’s validity window, took the account, and published malicious versions of eighteen packages including chalk and debug.

Those eighteen packages are downloaded around 2.6 billion times a week.

The payload was narrow#

The injected code was a crypto clipper. It ran in the browser, watched for wallet and web3 activity, and rewrote payment destinations to attacker-owned addresses.

For a compromise with that much reach it is a remarkably small ambition. It did nothing to the build machine, took no credentials, and left the server side alone. A payload that had gone after publishing tokens instead would have looked a great deal like the worm that showed up the same month.

Two hours#

The community spotted the code and raised it on GitHub within about two hours. Maintainers reverted to clean versions and the malicious releases were unpublished.

Two hours is a genuinely fast response. It is also long enough for every CI pipeline that resolved a fresh dependency tree that afternoon to have installed the bad version, which is the point people tend to miss when they cite the response time as reassurance.

What this says about 2FA#

The account had two-factor authentication and it did not help, because TOTP is phishable by design. The code proves possession of a shared secret at a moment in time; it does not prove what site you are giving it to. A convincing page on a look-alike domain collects it and replays it seconds later.

This is the specific failure that origin-bound credentials fix. A passkey or a hardware key is scoped to the domain that registered it, and simply produces nothing usable on npmjs.help. Same user, same mistake, no compromise.

The broader lesson is that publishing from a human account at all is the weak link – which is the case for trusted publishing, where the credential is a short-lived token minted by a CI provider for a specific workflow and cannot be phished out of a person because no person ever holds it.

That does not make the registry’s job disappear. Maintainers still need a way to recover an account, revoke a session, and get a clean release out quickly. But those controls are response tools. Making the normal publishing path resistant to replay is what keeps one convincing email from becoming an ecosystem event.

Sources#