On March 31, 2026, an attacker hijacked the npm account of axios's primary maintainer (jasonsaayman) and published two backdoored versions of the package — axios@1.14.1 and axios@0.30.4 — within 39 minutes of each other, covering both the current 1.x and legacy 0.x release branches simultaneously.
axios is one of the most widely used JavaScript libraries in existence, with over 100 million weekly downloads. The attack was not opportunistic — it was precision. The malicious dependency was staged 18 hours in advance, three platform-specific payloads were pre-built, and every artifact was designed to self-destruct after execution.
The malicious versions didn't modify axios itself. Instead, they injected a fake dependency — plain-crypto-js@4.2.1 — whose sole purpose was to run a postinstall script the moment anyone ran npm install. That script silently contacted a command-and-control server (sfrclak.com) and deployed a cross-platform Remote Access Trojan (RAT) targeting macOS, Windows, and Linux. After executing, the script deleted itself and replaced its own package.json with a clean decoy — meaning a forensic inspection of node_modules after the fact would show nothing suspicious.
The compromised versions were live for approximately three hours (00:21 to 03:29 UTC) before npm removed them. Any machine that ran npm install during that window — whether a developer laptop or a CI/CD pipeline — should be treated as fully compromised.
Tracked as: GHSA-fw8c-xr5c-95f9 and MAL-2026-2306
Work through the steps below on your local machine. If you find a compromised version at any point, skip to Step 6.
Run these from the root of your projects directory (e.g. ~/Sites, ~/Projects, or wherever your work lives). They scan package.json and package-lock.json for any reference to the affected versions:
grep -rnE --include="package.json" '"axios":\s*"[^"]*(1\.14\.1|0\.30\.4)"' .
grep -rnE --include="package-lock.json" '"axios":\s*"[^"]*(1\.14\.1|0\.30\.4)"' .Each flag and piece of the pattern has a specific job:
-r→ search recursively through all subdirectories-n→ include line numbers in the output so you can find the match quickly-E→ use extended regex, which allows the(a|b)alternation syntax--include="package.json"→ only look inside files namedpackage.json(ignores everything else)- The pattern
"axios":\s*"[^"]*(1\.14\.1|0\.30\.4)"matches any line where axios is set to one of the two compromised versions, including when prefixed with^or~(e.g."^1.14.1")
The second command is identical but targets package-lock.json files instead.
If you see file paths and line numbers printed — a match was found. Note the project path and move to Step 6.
If you see nothing printed — no matches were found. This is the result you want.
If you're using a terminal that color-codes command results (like Warp), you may see these commands highlighted in red even when everything is fine. That's because of how grep uses exit codes:
- Exit code 0 → matches were found
- Exit code 1 → no matches found ← this is the good result; red highlight is a false alarm
- Exit code 2 → an actual error occurred (e.g. bad flag, file not found)
Red highlighting on these commands almost certainly means no compromised versions were found — not that something went wrong.
If you want to see every project that has axios installed (not just the compromised versions), use this broader search:
grep -rnE --include="package.json" '"axios":\s*"' .Declared versions in package.json and what's physically installed in node_modules don't always match — someone may have run npm install before the lock file was updated, or a transitive dependency may have pulled in a different version. This step checks what's actually on disk:
find . -path "*/node_modules/axios/package.json" \
| xargs grep -l '"version"' \
| xargs grep -E '"version":\s*"(1\.14\.1|0\.30\.4)"'This is actually three commands chained together with pipes (|):
find . -path "*/node_modules/axios/package.json"→ locates every installed copy of axios across all nestednode_modulesdirectoriesxargs grep -l '"version"'→ filters that list to files that actually contain a"version"field (a sanity check — all validpackage.jsonfiles should)xargs grep -E '"version":\s*"(1\.14\.1|0\.30\.4)"'→ checks whether that version matches either compromised release
xargs is the glue — it takes the output of one command and passes it as arguments to the next.
If file paths are printed — an installed copy of the compromised version was found. Note which project directory it's in and move to Step 6.
If nothing is printed — no compromised versions are installed in node_modules. This is the result you want.
The same exit code behavior from Step 1 applies here: exit code 1 (no matches) may appear as red in Warp but is not an error.
Because the RAT self-deletes after running, the presence of plain-crypto-js in node_modules is itself a strong indicator of compromise — it is not a dependency of any legitimate axios version and has no reason to exist:
find . -path "*/node_modules/plain-crypto-js" -type dCaution
If this returns any results, move to Step 6 immediately.
Global npm packages are installed once on your machine and available in any project. It's less common to install axios globally, but worth ruling out:
npm list -g axiosnpm list -g→ lists all globally installed npm packagesaxios→ filters the output to only show axios and its version
If axios appears with a version number — check whether it matches 1.14.1 or 0.30.4. If it does, uninstall it:
npm uninstall -g axiosIf you see (empty) or axios isn't listed — nothing to do here.
Because the malware self-deletes, the absence of plain-crypto-js in node_modules does not mean a machine is clean. If a machine ran npm install during the three-hour exposure window, the RAT may have already executed and removed its own traces. Check for the known post-execution artifacts for your OS:
ls /Library/Caches/com.apple.act.mondThe RAT binary disguised itself as an Apple system process using this path. If the file exists, your machine is compromised.
Check for these files in PowerShell:
Test-Path "$env:PROGRAMDATA\wt.exe"
Test-Path "$env:TEMP\6202033.vbs"
Test-Path "$env:TEMP\6202033.ps1"Also check for a registry persistence key:
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "MicrosoftUpdate" -ErrorAction SilentlyContinueThe Windows variant establishes persistence via a registry Run key and a re-download batch file — unlike macOS and Linux, it survives reboots.
ls /tmp/ld.pyA Python-based RAT was written here. Note that the Linux variant crashes in containerized environments, so Docker/container-based CI runners may be less impacted.
If any of these files or keys exist — your machine is compromised. Do not attempt to clean in place. Move to Step 6.
If none exist — no post-execution artifacts were found. This is the result you want, though it does not fully rule out compromise if the RAT ran and completed its self-deletion.
Package managers cache downloaded tarballs locally so they don't have to re-download packages on every install. If a compromised version was downloaded at any point, it may still be sitting in your cache and could be used in a future install even after you've removed it from your projects.
Find any cached axios tarballs, then clear the full cache:
find ~/.npm -name "*.tgz" | grep axios
npm cache clean --forcefind ~/.yarn -name "*.tgz" 2>/dev/null | grep axios
yarn cache cleanfind ~/.yarn/cache -name "axios-*" 2>/dev/nullYarn Berry doesn't have a single cache clean command — if you find compromised tarballs, delete them directly by path.
find ~/.pnpm-store -path "*axios*package.json" 2>/dev/null \
| xargs grep -E '"version":\s*"(1\.14\.1|0\.30\.4)"' 2>/dev/null
pnpm store pruneThe find commands search your package manager's local cache directory for any axios tarballs (.tgz files) that were previously downloaded. If found, the cache clean commands that follow flush them out.
You'll notice 2>/dev/null appended to several commands. This redirects error output (file descriptor 2) to /dev/null — essentially a trash bin. It suppresses "no such file or directory" errors for cache paths that may not exist on your machine (e.g. if you don't use pnpm, ~/.pnpm-store won't exist). The command still runs normally; it just doesn't clutter your output with irrelevant errors.
If find returns file paths — those tarballs are cached. Run the corresponding cache clean command for your package manager.
If find returns nothing — your cache is clean.
Do not attempt to clean the machine in place. Rebuild from a known-good state (reimage or restore from a pre-compromise backup). Then rotate all credentials (see below) from a separate, clean machine.
Delete node_modules and your lock file, then reinstall after pinning a safe version:
rm -rf node_modules package-lock.json
npm installIf you use Yarn or pnpm, delete
yarn.lockorpnpm-lock.yamlinstead ofpackage-lock.json.
Before reinstalling, update package.json to explicitly set a known-good version. Using an exact version (no ^ or ~) prevents npm from silently upgrading to a compromised release in the future:
"axios": "1.7.9"The ^ (caret) and ~ (tilde) prefixes allow npm to install newer patch or minor versions automatically — ^1.7.9 means "any 1.x.x version." Removing them locks the install to exactly the version you specify.
If the affected machine or project had access to any of the following, treat them as fully compromised and rotate from a clean machine:
- npm tokens
- AWS / cloud provider credentials
- SSH keys
- CI/CD secrets and environment variables
.envvalues of any kind- API keys stored locally or in project files
The RAT was live, beaconing to a remote server, and capable of executing arbitrary follow-on commands. Anything that was accessible on the machine during the exposure window should be considered exfiltrated.
Add the following to your DNS blocklist or firewall if you haven't already:
- Domain:
sfrclak.com - IP:
142.11.206.73
Let the team know which project(s) or machine(s) were affected so we can assess broader exposure and determine whether any shared credentials or CI/CD pipelines need to be audited.
| Version | Status |
|---|---|
0.30.4 |
⛔ Compromised — do not use |
1.14.1 |
⛔ Compromised — do not use |
0.30.3 |
✅ Known safe (0.x branch) |
1.14.0 |
✅ Known safe (1.x branch) |
1.7.9 |
✅ Known safe (stable) |
For the latest verified release, check the official axios releases page.
- Axios npm Package Compromised: Supply Chain Attack Delivers Cross-Platform RAT — Snyk, March 31, 2026
- axios Compromised on npm: Malicious Versions Drop Remote Access Trojan — StepSecurity, March 30, 2026
- Inside the Axios Supply Chain Compromise: One RAT to Rule Them All — Elastic Security Labs, March 31, 2026
- Compromised axios npm Package Delivers Cross-Platform RAT — Datadog Security Labs, March 31, 2026