On March 31, 2026, between 00:21 UTC and roughly 03:15 UTC, two versions of axios sat on the npm registry with an extra dependency declared in package.json: plain-crypto-js@4.2.1. That dependency was never imported at runtime. It only existed so npm would resolve it, install it, and trigger its postinstall script, which downloaded and ran a cross-platform RAT (Remote Access Trojan, malware that gives the attacker remote, persistent control of the target machine) the moment you ran npm install. The compromised releases were axios@1.14.1 and axios@0.30.4, published from the account of the project’s lead maintainer, which had been taken over by an actor tied to North Korea.
The interesting thing about this incident is not that npm is unsafe. It is where trust in npm actually lives. When your team installs axios, the decision is not “I trust this library”. It is “I trust everyone who can publish it, every transitive dependency it pulls in, and every install-time script any of those dependencies may run”. This post is about what the incident actually exposed and which operational defenses move the needle for a small team, without turning into security theater.
What happened, in order#
The timeline matters because it explains why so many environments got exposed without anyone noticing.
- On March 30, the attacker published
plain-crypto-js@4.2.0, a clean release that mimicked the legitimatecrypto-jsname. It sat there unnoticed. - Later that day, they published
plain-crypto-js@4.2.1, this time with a maliciouspostinstall. - On March 31 at 00:21 UTC, they published
axios@1.14.1from the compromised maintainer account. About 39 minutes later, they publishedaxios@0.30.4. - Both releases added
"plain-crypto-js": "^4.2.1"topackage.json. In practice, anynpm installthat resolved one of those versions pulled in the payload. - Around 03:15 UTC, npm removed both malicious axios versions. Total exposure window: about three hours.
- The maintainer account had its email changed to an attacker-controlled address before the publishes, and the access came from a mix of social engineering and a RAT running on the maintainer’s personal machine.
Three details are worth flagging. First, the malicious transitive package was planted earlier so it would look like a normal dependency. Second, this was not a typosquat: the compromised package was the real axios, pushed from the real maintainer account. Third, three hours on npm is plenty of time to hit a lot of people, because CI pipelines run constantly and install transitive dependencies without curation.
Why nobody caught it in time#
There is a comforting reading of this incident that says “just review your dependencies”. It does not survive the technical detail.
The malicious dependency was not visible in the axios source. No .js file in axios ran require('plain-crypto-js'). It only existed in the package.json of the new releases, and the payload did not fire when axios was used at runtime. It fired when npm install resolved the tree and executed the postinstall of the transitive package. Reading axios source code would not surface the RAT. Reading the package.json would surface one extra dependency with a plausible name.
Add to that three habits almost every team has:
^1.14.0inpackage.json, which accepts any new minor or patch automatically.- Environments that run
npm installinstead ofnpm ci, which lets a newer version slip in even with a lockfile present. - CI that runs install scripts by default, with no
--ignore-scriptsflag.
With those three in place, the attacker needs the victim to do nothing outside their normal workflow. Publishing the new version is enough. The next build picks it up.
Where trust in npm actually lives#
Naming this matters, because the name changes what the team chooses to monitor.
When a project depends on axios, the real trust surface includes, at minimum:
- the npm account of every maintainer with publish rights
- the email address and 2FA method attached to those accounts
- the internal pipeline the project itself uses to publish to npm
- every direct dependency declared in its
package.json - every transitive dependency pulled in recursively
- every
postinstall,preinstall, andinstallscript any package in the tree runs
None of that shows up in import axios from 'axios'. All of it is assumed trustworthy by default. The March 2026 case attacked two links at once: the maintainer account and a transitive dependency with a postinstall.
What teams could have done beforehand#
This is the useful part. None of the defenses below are exotic, and none of them require expensive tooling. They just have to actually be turned on.
| Defense | What it blocks | Practical cost |
|---|---|---|
npm ci instead of npm install in CI | Resolving a version outside the lockfile | None, this is the correct behavior for reproducible builds |
--ignore-scripts in CI | Running postinstall, preinstall, and install hooks from dependencies | Requires auditing packages that genuinely need install scripts (rare) |
| Committed lockfile reviewed in PRs | Swapping a transitive dependency with no review | Needs a habit of reading lockfile diffs |
Pinned versions (no ^ or ~) on sensitive deps | Silent automatic upgrades | More bump PRs, ideally automated and reviewed |
| Minimum release age policy (e.g. 7 days) | Consuming a compromised release during its first hours | Some SCA (software composition analysis) tools ship this as a feature |
npm overrides to force a known-good version | Sneaking in through a transitive dependency | Low, lives in the project’s package.json |
| Strong 2FA and hardware keys on maintainer accounts | Account takeover via credential theft | Low for the team, high payoff for the ecosystem |
The general rule: the more a pipeline relies on automatic decisions from npm, the more attack surface it carries. Every row in that table removes one of those automatic decisions.
What to do now, if you built anything that day#
If there is any chance your team resolved axios@1.14.1 or axios@0.30.4 between March 31 at 00:21 UTC and roughly 03:15 UTC, the reasonable response is this:
- Grep lockfiles and CI history for
axios@1.14.1,axios@0.30.4, or any occurrence ofplain-crypto-js. - Check every involved machine for a
node_modules/plain-crypto-jsdirectory that exists or ever existed. Its presence means the install ran. - Look for outbound connections to
sfrclak.comor142.11.206.73on port8000in the last few weeks of network logs. - On any machine with evidence, assume compromise and rotate npm tokens, SSH keys, cloud credentials, CI secrets, and anything else the affected user account could reach.
- Roll back to safe versions:
axios@1.14.0on the 1.x line,axios@0.30.3on the 0.x line. - Optionally add
"overrides": { "axios": "1.14.0" }topackage.jsonwhile the rollback settles.
That is the minimum defensible sequence. In sensitive environments, reformatting the affected developer’s machine is the standard call, because the payload was a RAT with persistence.
The broader lesson about trusting npm#
The axios case is not a weird outlier. It is a reminder that trust in npm is a chain, not a badge. Maintainer, account, 2FA, the maintainer’s personal machine, direct dependencies, transitive dependencies, install-time scripts: each link can fail on its own and take the rest down with it.
The fix is not heroic either. It is operational and a little boring: npm ci, --ignore-scripts in CI, a reviewed lockfile, pinned versions where it matters, overrides to contain transitive surprises, and a short waiting period before adopting a brand-new release. Those five or six decisions together would have sharply reduced the blast radius of this incident for most teams.
If you have time for exactly one action today, make it this: swap npm install for npm ci --ignore-scripts in your CI, and review the lockfile diff in your next PR. Together, those two changes already close most of the surface the axios case exploited.
References#
- Post Mortem: axios npm supply chain compromise (axios/axios#10636)
- Mitigating the Axios npm supply chain compromise — Microsoft Security Blog
- North Korea-Nexus Threat Actor Compromises Widely Used Axios NPM Package — Google Cloud Threat Intelligence
- Axios npm Package Compromised: Supply Chain Attack Delivers Cross-Platform RAT — Snyk
- axios Compromised on npm — StepSecurity

Comments
Comments use Disqus and load only if you click the button below.