On March 31, 2026, at 00:21 UTC, two versions of axios appeared on the npm registry: axios@1.14.1 and axios@0.30.4. By 03:25 UTC — three hours and four minutes later — npm had pulled them.
Axios receives roughly 100 million downloads per week. Both the 1.x and 0.x release branches were hit within 39 minutes of each other.
The attack chain
Account takeover. Attackers gained control of the npm account by changing the registered email to an anonymous ProtonMail address. This bypassed the package's GitHub Actions CI/CD safeguards entirely — npm publishing credentials are separate from GitHub.
Infrastructure staged 18 hours in advance. The malicious dependency was published to the registry before the axios packages. The attack was not opportunistic. Someone pre-built three OS-specific payloads and executed at a chosen moment.
A fake dependency as the dropper. Rather than modifying the axios source directly, the attackers added a new entry to package.json: plain-crypto-js@4.2.1. It had never been in the axios codebase. Its only function was to run a post-install script that fetched and executed the real payload. Post-install scripts execute automatically during npm install, with the privileges of the running user — in CI/CD, that typically means access to secrets, credentials, and internal network resources.
Three payloads, cross-platform. Separate implementations targeted macOS, Windows, and Linux. All three included self-destruct mechanisms to erase evidence after persistence was established. The RAT provided full remote access to source code, cloud credentials, API keys, and anything in the build environment. Google Threat Intelligence attributed the attack to UNC1069, a suspected North Korean threat actor focused on cryptocurrency theft via supply chain compromise.
Why the three-hour window was enough
Modern CI/CD pipelines run npm install on every build, pulling whatever version satisfies the declared range. A project using "axios": "^1.14.0" would automatically receive 1.14.1 on the next build.
Three hours covers every project running automated builds, every developer running npm install, every Docker build pulling fresh dependencies during that window. If your build ran between 00:21 and 03:25 UTC, you installed the backdoor.
What defenders could have done differently
Use npm ci, not npm install. This is the single most impactful defense. npm ci installs exactly what the lockfile specifies and fails if the lockfile doesn't match. A project using npm ci with an existing lockfile would not have installed 1.14.1 — it was not in the lockfile. Most projects do not use npm ci by default. They should.
Block post-install scripts. There is no legitimate reason for a pure JavaScript HTTP client to run a post-install script. npm install --ignore-scripts or ignore-scripts=true in .npmrc blocks this class of attack. It breaks packages with native bindings; for everything else, it is a strong control.
SCA in CI. plain-crypto-js@4.2.1 had zero download history, no GitHub repository, no prior versions, and a post-install script. Any of these are individually suspicious. All together, they would have flagged the package before your pipeline ran it.
An SBOM as a forensic anchor. An organization with a versioned SBOM from March 30 could diff it against March 31 and immediately see plain-crypto-js appear where it was not before. Without an SBOM, determining exposure meant relying on developer memory — neither npm audit nor git history catches a malicious transitive dependency present for only three hours.
If you were affected
If a build ran between 00:21 and 03:25 UTC on March 31: assume compromise. The self-destruct mechanism means forensic traces may be absent.
Rotate everything — cloud provider keys, npm tokens, GitHub PATs, any secrets in environment variables or .env files on affected machines. Rebuild developer machines from scratch. Audit CI/CD runners for unexpected network connections. Update axios to 1.14.2 or 0.30.5.
The axios incident is one of a recurring pattern: event-stream (2018), ua-parser-js (2021), colors and faker (2022), now axios (2026). The structural weakness is the same each time — implicit trust in package registries. The fastest defense available today is knowing exactly what is in your dependency tree and being alerted the moment something changes.
Lumstep runs software composition analysis and dependency trust scoring on every scan. Trust scores flag packages with no source code, suspicious post-install scripts, or anomalous publish behavior — the signals that would have caught plain-crypto-js before your pipeline ran it.
Ou laissez Lumstep s'en charger.
Connectez un repo et Lumstep le scanne automatiquement - secrets, dépendances, SBOM et qualité du code - et ouvre la PR de correction.