Teardown: the axios npm compromise

One new dependency, three hours on latest, a trojan on every OS.

What happened on 31 March 2026, how a maintainer's laptop became a publish key, and where Chainsaw would and would not have stopped it.

Questions and answers

What happened?

The axios npm compromise is the 31 March 2026 attack in which two malicious versions of axios were published to npm from the lead maintainer’s own account. The first, 1.14.1, went up at 00:21 UTC and was tagged latest; the second, 0.30.4, followed at about 01:00 UTC. Axios gets over 100 million downloads a week, so for roughly three hours an unpinned npm install axios pulled the malicious release. The malicious axios release added one new dependency, plain-crypto-js@4.2.1, whose postinstall hook installed a remote-access trojan on macOS, Windows and Linux. npm removed the versions and the injected dependency between about 03:15 and 03:40 UTC. CISA published an alert on 20 April 2026, and the timeline here follows the axios incident report and the analysis by Datadog Security Labs.

How did the attacker get publish rights?

The axios attacker got npm publish rights through the lead maintainer’s own computer. According to the axios incident report, a targeted social-engineering campaign first got remote-access malware onto the lead maintainer’s PC, and the malware gave the attacker the npm credentials used to publish on 31 March 2026. Two-factor authentication was on and did not help: the attacker was already on the maintainer’s machine.

One detail stands out: 1.14.0 had been published through GitHub Actions, by the project’s own workflow using npm trusted publishing. The two malicious versions were published straight from the account, outside the CI pipeline. Chainsaw’s publisher-change signal fires on 1.14.1 for exactly this change, but the rule built on the signal ships in monitor mode, so on its own the rule records the change and lets the install through.

What changed in the package?

The axios compromise changed one line of the package: a new dependency. Datadog compared the code of 1.14.1 with 1.14.0 and found the two versions the same except for the added dependency, plain-crypto-js@4.2.1. Nothing in axios itself was malicious. The attack sat one level down, in a transitive package where few people look.

plain-crypto-js was a brand-new package in March 2026. The first version appeared on npm about 18 hours before the malicious axios release, and 4.2.1, the one axios pulled in, 22 minutes before it. Package age is what a Chainsaw default rule keys on: any package first published less than a day ago is refused by Chainsaw, so through the Chainsaw proxy the dropper would have failed on first download.

What ran on install?

An ordinary npm install axios ran a postinstall hook in the injected dependency, plain-crypto-js@4.2.1, and the hook ran an obfuscated setup.js on 31 March 2026. According to Datadog’s analysis, setup.js branched by operating system:

  • macOS: an AppleScript dropper fetched a binary into /Library/Caches/com.apple.act.mond and ran it.
  • Windows: a copy of PowerShell fetched a script payload and set a Run key so it survived reboots.
  • Linux: the script downloaded /tmp/ld.py and ran the file with python3.

Each payload is a remote-access trojan that reported to sfrclak[.]com on port 8000. Finally, setup.js deleted itself and swapped in a clean package.json, so a look inside node_modules after the fact showed no install hook. CISA recommends ignore-scripts=true in .npmrc, which stops npm from running install hooks at all, including the hook in the axios dropper.

Would Chainsaw have stopped it?

Yes. Chainsaw would have stopped the axios compromise on day 0, for installs that went through the Chainsaw proxy. The result comes from our re-run of 58 attacks from 2026 against production Chainsaw (v0.22.118) on 19 October 2026:

  • The dropper was refused on first download. A default rule refuses any package first published less than a day ago. plain-crypto-js was about 18 hours old when axios started pulling it, so the install failed before setup.js could run.
  • The malicious axios versions were held back. Chainsaw holds every new version for 48 hours by default, so npm install axios resolved to 1.14.0. The known-malicious feed listed both versions 2 to 20 hours after publish, so they stay refused.
  • The publisher change is flagged, not refused. Chainsaw’s publisher-change signal fires on 1.14.1, but the rule that uses it ships in monitor mode, so on its own the rule records the change and lets the install through.
  • The install script alone would not have been refused. setup.js decodes its strings and runs commands without a direct download call, which the install-script rule does not treat as a remote fetch. The age rule and the hold are what stopped the axios attack.
  • Installs that bypassed the proxy got the malware. A machine pointed straight at registry.npmjs.org was not protected. That is why Chainsaw also gates CI, pins laptops through MDM and blocks direct registry traffic at the network.

What should you do?

If a machine or CI runner installed axios@1.14.1 or axios@0.30.4, the first step given by CISA is to treat the machine as compromised, because the axios dropper, plain-crypto-js, installed a remote-access trojan during npm install. Then, per CISA’s advice of 20 April 2026, downgrade to 1.14.0 or 0.30.3, delete node_modules/plain-crypto-js/, rotate every credential the machine could reach, and block sfrclak[.]com.

For the next one:

  1. Don’t install brand-new packages or versions straight away. CISA suggests a seven-day minimum release age in .npmrc. A day would have been enough here.
  2. Check how a release was published, not only who published it. A popular package that suddenly stops publishing through its CI pipeline deserves a hold.
  3. Turn off install scripts where you can. CISA recommends ignore-scripts=true. Some packages need their scripts, so this is easier in CI than on laptops.
  4. Use phishing-resistant MFA on developer accounts. MFA would not have saved a maintainer whose machine was already compromised, but it closes the cheaper routes in.

Sources

One teardown per notable attack

Get the next teardown by email

What happened, the install-path angle most coverage misses, and the rule that refuses it.