LLM prompt: Investigate axios supply chain attack (March 2026) across repos and machines
You are investigating whether any repositories or machines have been compromised by the axios npm supply chain attack discovered on March 31, 2026. Follow these instructions precisely.
On March 31, 2026, the npm account of axios maintainer "jasonsaayman" was hijacked. Two malicious axios versions were published containing a dependency on plain-crypto-js@4.2.1, which drops a cross-platform Remote Access Trojan (RAT).
axios@1.14.1(published 2026-03-31 00:21 UTC)axios@0.30.4(published 2026-03-31 01:00 UTC)plain-crypto-js@4.2.1(the RAT dropper, published 2026-03-30 23:59 UTC)@shadanai/openclaw@2026.3.28-2,@shadanai/openclaw@2026.3.28-3,@shadanai/openclaw@2026.3.31-1,@shadanai/openclaw@2026.3.31-2@qqbrowser/openclaw-qbot@0.0.130
Safe axios versions: 1.14.0, 0.30.3, or any version before these malicious ones. Clean fix version: 1.14.2.
| OS | Payload Path |
|---|---|
| macOS | /Library/Caches/com.apple.act.mond |
| Windows | %PROGRAMDATA%\wt.exe and %PROGRAMDATA%\system.bat |
| Linux | /tmp/ld.py |
- C2 server:
sfrclak.com:8000 - C2 endpoints:
packages.npm.org/product0(macOS),packages.npm.org/product1(Windows),packages.npm.org/product2(Linux) - Attacker email:
ifstap@proton.me,nrwise@proton.me
The malicious plain-crypto-js package runs a postinstall script (setup.js) that:
- Detects the OS
- Drops an OS-specific RAT binary/script
- Cleans up forensic evidence (renames
package.jsontopackage.md, removes postinstall hooks) - The RAT beacons to C2 every 60 seconds and supports: shell command execution, file system enumeration, payload execution, and (on Windows) in-memory DLL loading
These checks determine if the machine itself has been compromised, regardless of current repo state. Run these first.
# 1. Check for RAT payload artifacts
echo "=== RAT Payload Check ==="
# macOS
if [ -f "/Library/Caches/com.apple.act.mond" ]; then
echo "CRITICAL: macOS RAT payload found at /Library/Caches/com.apple.act.mond"
else
echo "OK: No macOS RAT payload found"
fi
# Linux
if [ -f "/tmp/ld.py" ]; then
echo "CRITICAL: Linux RAT payload found at /tmp/ld.py"
else
echo "OK: No Linux RAT payload found"
fi
# 2. Check npm cache for malicious packages
echo ""
echo "=== npm Cache Check ==="
npm cache ls 2>/dev/null | grep -iE "axios.*(1\.14\.1|0\.30\.4)|plain-crypto-js" && \
echo "WARNING: Malicious package found in npm cache" || \
echo "OK: npm cache clean"
# 3. Check for active C2 connections
echo ""
echo "=== Active Network Connections ==="
(lsof -i -nP 2>/dev/null || ss -tunap 2>/dev/null) | grep -iE "sfrclak|8000" && \
echo "CRITICAL: Active C2 connection detected" || \
echo "OK: No active C2 connections found"
# 4. Check DNS cache/logs if available
echo ""
echo "=== DNS Check ==="
(dscacheutil -cachedump 2>/dev/null; cat /var/log/syslog 2>/dev/null) | grep -i "sfrclak" && \
echo "WARNING: DNS resolution to C2 domain found" || \
echo "OK: No C2 domain DNS activity found"
# 5. Check running processes
echo ""
echo "=== Suspicious Process Check ==="
ps aux | grep -E "com\.apple\.act\.mond|wt\.exe|ld\.py" | grep -v grep && \
echo "CRITICAL: Suspicious process running" || \
echo "OK: No suspicious processes found"For each repository, run these checks. If you have access to multiple repos, run them in parallel using background agents.
Important: gaxios (Google's HTTP client) is NOT the same as axios. Do not flag gaxios as a finding.
REPO_PATH="<absolute path to repo>"
echo "=== Investigating: $REPO_PATH ==="
# 1. Check if axios exists in any current package.json
echo "--- Current package.json files ---"
find "$REPO_PATH" -name "package.json" -not -path "*/node_modules/*" -exec grep -l '"axios"' {} \;
# 2. Check if axios exists in any lock files (npm, yarn, pnpm, bun, deno)
echo "--- Current lock files ---"
find "$REPO_PATH" \( -name "package-lock.json" -o -name "yarn.lock" -o -name "pnpm-lock.yaml" -o -name "bun.lock" -o -name "deno.lock" \) -not -path "*/node_modules/*" -exec grep -l "axios" {} \;
# 3. Check node_modules for axios
echo "--- node_modules check ---"
find "$REPO_PATH" -path "*/node_modules/axios/package.json" -exec grep '"version"' {} \;
# 4. Check for plain-crypto-js (the malicious payload package)
echo "--- plain-crypto-js check ---"
find "$REPO_PATH" -path "*/node_modules/plain-crypto-js" -type d
grep -rl "plain-crypto-js" "$REPO_PATH" --include="package*.json" --include="yarn.lock" --include="pnpm-lock.yaml" --include="bun.lock" --include="deno.lock" 2>/dev/null
# 5. Check Deno config files for axios (deno.json, deno.jsonc, import_map.json)
echo "--- Deno config check ---"
find "$REPO_PATH" \( -name "deno.json" -o -name "deno.jsonc" -o -name "import_map.json" \) -exec grep -l "axios" {} \; 2>/dev/null
# 6. Check for transitive axios dependencies (packages that depend on axios)
echo "--- Transitive axios dependencies ---"
cd "$REPO_PATH"
if [ -f "package-lock.json" ] || [ -d "node_modules" ]; then
npm ls axios 2>/dev/null | head -30
elif [ -f "yarn.lock" ]; then
yarn why axios 2>/dev/null | head -30
elif [ -f "pnpm-lock.yaml" ]; then
pnpm why axios 2>/dev/null | head -30
elif [ -f "bun.lock" ] || [ -f "bun.lockb" ]; then
bun pm ls 2>/dev/null | grep -i axios | head -30
elif [ -f "deno.lock" ]; then
deno info 2>/dev/null | grep -i axios | head -30
fiIf none of the above return results: the repo is CLEAN — skip to the report.
REPO_PATH="<absolute path to repo>"
# 1. Check exact axios versions in lock files
echo "--- Installed axios versions ---"
find "$REPO_PATH" -name "package-lock.json" -not -path "*/node_modules/*" -exec sh -c '
echo "File: $1"
python3 -c "
import json, sys
with open(sys.argv[1]) as f:
data = json.load(f)
pkgs = data.get(\"packages\", data.get(\"dependencies\", {}))
for key, val in pkgs.items():
if \"axios\" in key.lower() and \"gaxios\" not in key.lower():
ver = val.get(\"version\", \"unknown\")
print(f\" {key}: {ver}\")
" "$1"
' _ {} \;
# 2. Check if malicious versions were EVER installed (git history)
echo "--- Git history check for malicious versions ---"
cd "$REPO_PATH"
git log --all -p -S "1.14.1" -- "*/package-lock.json" "*/yarn.lock" "*/pnpm-lock.yaml" "*/bun.lock" "*/deno.lock" "package-lock.json" | grep -B5 -A5 "axios.*1\.14\.1"
git log --all -p -S "0.30.4" -- "*/package-lock.json" "*/yarn.lock" "*/pnpm-lock.yaml" "*/bun.lock" "*/deno.lock" "package-lock.json" | grep -B5 -A5 "axios.*0\.30\.4"
# 3. Check for plain-crypto-js in git history
echo "--- Git history check for plain-crypto-js ---"
git log --all -p -S "plain-crypto-js" -- "*/package*.json" "*/yarn.lock" "*/pnpm-lock.yaml" "*/bun.lock" "*/deno.lock"
# 4. Check for the forensic cleanup signature (package.json renamed to package.md)
echo "--- Forensic cleanup artifacts ---"
find "$REPO_PATH" -name "package.md" -path "*/node_modules/*"
# 5. Check postinstall scripts in node_modules packages
echo "--- Suspicious postinstall scripts ---"
find "$REPO_PATH" -path "*/node_modules/*/package.json" -exec grep -l "setup\.js" {} \; 2>/dev/null | head -20
# 6. Check if axios version range could resolve to malicious version
echo "--- Version range analysis ---"
find "$REPO_PATH" -name "package.json" -not -path "*/node_modules/*" -exec sh -c '
python3 -c "
import json, sys
with open(sys.argv[1]) as f:
data = json.load(f)
for section in [\"dependencies\", \"devDependencies\", \"peerDependencies\", \"optionalDependencies\"]:
deps = data.get(section, {})
if \"axios\" in deps:
ver = deps[\"axios\"]
print(f\" {sys.argv[1]}: axios {ver} in {section}\")
# Flag ranges that could resolve to malicious versions
if any(c in ver for c in [\"^\", \"~\", \"*\", \">=\", \"latest\"]):
print(f\" WARNING: Range \\\"{ver}\\\" may have resolved to 1.14.1 or 0.30.4\")
" "$1" 2>/dev/null
' _ {} \;
# 7. Check transitive dependencies that pull in axios
echo "--- Transitive dependency check ---"
cd "$REPO_PATH"
if [ -f "package-lock.json" ] || [ -d "node_modules" ]; then
echo "npm dependency tree for axios:"
npm ls axios 2>/dev/null || echo " (could not resolve full tree — check lock file manually)"
echo ""
echo "npm why axios:"
npm why axios 2>/dev/null || true
elif [ -f "yarn.lock" ]; then
echo "yarn dependency tree for axios:"
yarn why axios 2>/dev/null || true
elif [ -f "pnpm-lock.yaml" ]; then
echo "pnpm dependency tree for axios:"
pnpm why axios 2>/dev/null || true
elif [ -f "bun.lock" ] || [ -f "bun.lockb" ]; then
echo "bun dependency tree for axios:"
bun pm ls 2>/dev/null | grep -i axios || true
elif [ -f "deno.lock" ] || [ -f "deno.json" ] || [ -f "deno.jsonc" ]; then
echo "Deno dependency info for axios:"
# Check deno.json/deno.jsonc imports map for axios
find "$REPO_PATH" \( -name "deno.json" -o -name "deno.jsonc" \) -exec grep -A2 "axios" {} \; 2>/dev/null || true
# Check deno.lock for resolved axios versions
if [ -f "deno.lock" ]; then
python3 -c "
import json, sys
with open('deno.lock') as f:
data = json.load(f)
# Check npm packages in deno.lock
for section in ['packages', 'npm']:
if section in data:
items = data[section]
if isinstance(items, dict):
for key, val in items.items():
if 'axios' in key.lower() and 'gaxios' not in key.lower():
print(f' {key}: {val}')
" 2>/dev/null || true
fi
fi
# 8. Check CI config for any axios installs
echo "--- CI pipeline check ---"
find "$REPO_PATH" \( -name "*.yml" -o -name "*.yaml" \) -path "*/.github/*" -exec grep -l "axios" {} \; 2>/dev/null
find "$REPO_PATH" \( -name "Dockerfile*" -o -name "*.dockerfile" \) -exec grep -l "axios" {} \; 2>/dev/nullTo scan all repos in a directory (e.g., ~/repos/):
# List all git repos under a parent directory
find ~/repos -maxdepth 2 -name ".git" -type d | sed 's/\/.git$//' | sortThen launch one background agent per repo using the Phase 2 checks. Each agent should output a single-line verdict at the end.
For each repository, produce a report in this exact format:
## <repo-name>
- **Status:** CLEAN | INVESTIGATE | COMPROMISED
- **Axios dependency:** None | Direct (version) | Transitive (via package)
- **Malicious versions in history:** Yes/No
- **plain-crypto-js found:** Yes/No
- **Action required:** None | [specific actions]
For machine-level checks:
## Machine: <hostname>
- **RAT payload:** Not found | FOUND at <path>
- **npm cache:** Clean | Contains malicious packages
- **C2 connections:** None | ACTIVE
- **Suspicious processes:** None | FOUND
- **Action required:** None | [specific actions]
If ANY check returns positive:
- IMMEDIATE: Isolate the machine — disconnect from network
- Rotate ALL secrets — npm tokens, SSH keys, cloud credentials, API keys, database passwords. Assume any credential accessible from the machine is compromised
- Remove RAT payloads — delete the files listed in the artifacts table above
- Kill malicious processes —
kill -9anycom.apple.act.mond,wt.exe, orld.pyprocesses - Clean npm —
npm cache clean --force, removenode_modules, delete lock files, reinstall - Pin axios — lock to
1.14.2or1.14.0(not^1.14.0which could re-resolve) - Block C2 — add
sfrclak.comandpackages.npm.orgto firewall/DNS blocklist - Audit git history — check if any commits were made by the attacker during the compromise window (March 30-31, 2026)
- Report — file an incident report and notify your security team
- GitHub Issue: axios/axios#10604
- StepSecurity Analysis: https://www.stepsecurity.io/blog/axios-compromised-on-npm-malicious-versions-drop-remote-access-trojan
- The Hacker News: https://thehackernews.com/2026/03/axios-supply-chain-attack-pushes-cross.html