Package Upgrades Feel Like Russian Roulette
Today a hijacked maintainer account published malicious Axios versions . 83 million weekly downloads. The payload was a cross-platform RAT, staged 18 hours in advance , hitting both release branches within 39 minutes, self-deleting after execution.
The Axios attack used a stolen npm access token, not a password compromise. The attacker bypassed CI/CD entirely and published directly to the registry. 2FA on the account wouldn’t have stopped a stolen automation token. In September 2025, 27 npm packages with 2.6 billion aggregate weekly downloads were compromised through similar credential theft. The Shai-Hulud worm hit 796 packages .
I ship a desktop plugin. We deliberately don’t auto-update because we don’t push code to someone’s machine without their consent. If a bad dep makes it into a release, we can’t silently patch it out.
What we do and where it breaks down
We delay dependency updates by 14 days, betting that compromised packages get caught before we pull them in. Sometimes that works: node-tar (CVE-2026-23745) was discovered and patched the same day. Sometimes it doesn’t: Shai-Hulud ran for weeks before detection. There is no reliable dwell time for npm malware.
We run npm audit on every CI build. That catches known CVEs. It’s useless against a freshly compromised package that hasn’t been flagged yet, which is exactly the Axios scenario.
Neither of these mitigations address credential theft at the maintainer level. They’re downstream defenses against an upstream problem.
What actually reduces the risk
Every package in our dependency tree is a maintainer account that could get phished. We’ve been reducing that number. Fewer packages, fewer accounts, fewer tokens that could leak.
And it doesn’t help if the one package we keep is the one that gets hit. There’s no complete solution here, which is the uncomfortable part.
What I’d like npm to fix
Automation tokens need scoping. The Axios attacker used a long-lived classic token with full publish access. Granular access tokens exist but aren’t required. A package with 83M weekly downloads should require tokens scoped to specific versions or CI environments, not skeleton keys.
Default to deterministic installs. npm ci installs exactly what’s in your lockfile, nothing more. npm install resolves new versions, which means a compromised version published after your last lockfile update can silently enter your build. Most developers use npm install. The default should not be the dangerous option.