The axios npm Supply Chain Attack and What It Exposed

php2shell patches (CVE-2026-109618 and CVE-2026-109619) for EOL PHP are out now. With sales replies slower than usual due to high demand, the fastest route to patched packages is to buy ELS for Runtimes online (opens in new tab). Read the patch FAQ

Go straight to an AI summary of this blog post instead

  • Summarize this post with Perplexity
  • Summarize this post with Gemini

Key Takeaways

  • The malicious axios versions were live for only about three hours, so installs that happened before detection got no help from advisory-driven defenses.
  • Download counts measure adoption, not review, and axios can arrive through packages a team never picked.
  • Several controls could have broken this chain, from locked installs and blocked install scripts to quarantine windows, provenance checks, and registry screening.
  • The strongest preventive controls sit at or before install time, which is where many teams have invested the least.

Two malicious versions of axios went live on March 31, 2026, and stayed up for roughly three hours before anyone pulled them. In that window they slipped quietly into dependency trees across a huge chunk of the JavaScript ecosystem, and almost nobody who installed them had a way to know.

No one on the project published them. The releases came out of a hijacked maintainer account, published by hand with a stolen long-lived token that went around the project’s own CI pipeline, so on the registry they looked like ordinary axios releases. The version numbers looked normal. npm audit had nothing to say for the first stretch of the window, because the advisory that would eventually describe the incident hadn’t been written yet.

Three Hours Inside an npm Supply Chain Attack

Three hours isn’t long. It’s not long enough for most teams to notice a new release went out, let alone evaluate it.

The general shape of a publish-side compromise is well documented, and axios followed the outline with one extra layer that the generic version misses. Both releases pulled in a brand-new dependency called plain-crypto-js, which existed for no reason other than to run a payload from a postinstall hook. A clean decoy version of that package had gone up about 18 hours earlier, which made the malicious follow-up look less like something brand new, and the axios source files themselves were never touched. Social engineering got the attacker onto the maintainer’s machine first, and the publish rights came with it.

Phase What happens Detection surface
Access Publish credentials or a CI token get stolen Registry audit logs, if anyone’s reading them
Publish A version goes out that doesn’t match the public repo Provenance attestation, if it’s turned on
Propagation Semver ranges and fresh installs pull the new version No check in the default npm install path
Discovery A researcher or maintainer spots the mismatch Community reporting
Removal Version gets unpublished or deprecated Registry metadata, security tooling, and advisory feeds

 

The interesting phase is propagation. Between publish and discovery, the default npm install path never asks a package to prove that the artifact coming down the wire was built from the source and workflow it claims. That gap is the entire attack window.

Wiz saw the malware actually execute in around 3% of affected environments that turned up in its telemetry, and researchers separately put the exposure window at an estimated 600,000 downloads. The two figures come from different populations, so multiplying them together doesn’t produce a real headcount. Read side by side, though, they describe confirmed payload execution inside a very short exposure window, not just files sitting on disk.

Reality Check: Unpublishing a malicious version stops future installs. It doesn’t do anything for machines that already ran it, tokens that already got exfiltrated, or container images already built and pushed with the bad code baked in.

Why 100 Million Weekly Downloads Is Not a Security Signal

The unspoken assumption behind picking popular dependencies is that popularity means scrutiny. Many eyes, shallow bugs, that whole line of thinking.

It’s a reasonable argument about source code. It falls apart for publish integrity, because the eyes are on the repository while the attack happens at the registry. Those are two different artifacts, and npm doesn’t require them to match. Plenty of scanners were watching that night and several of them flagged the release within minutes. Nothing in the default install path waits for them.

There’s a second problem, and it’s the one that makes this kind of incident so annoying to respond to. Some teams never installed axios directly at all. They installed something that installed something else that installed axios.

Endor Labs research cited in OWASP’s Top 10 for Open Source Software puts the share of vulnerabilities living in indirect dependencies at roughly 95% rather than in the packages a team chose on purpose. That figure counts known vulnerabilities rather than malware, but it shows how much of a dependency graph sits outside the packages developers pick on purpose, and those graphs haven’t gotten any shallower. When the compromised package sits four layers down in a graph nobody has actually read, “Do we even use axios?” turns into a surprisingly hard question to answer fast.

npm ls axios –all

npm ls plain-crypto-js –all

Run those against every repo, not just the ones you remember adding an HTTP client to. If either one comes back with axios 1.14.1 or 0.30.4, or plain-crypto-js 4.2.1, treat the host as compromised, revert to axios 1.14.0 or 0.30.3, and rotate every credential that host could reach.

What Did a State Actor Want With an HTTP Client?

Google’s Threat Intelligence Group attributed the axios compromise to UNC1069, a financially motivated North Korea-linked actor with a track record of stealing from cryptocurrency exchanges. Microsoft tracks overlapping activity as Sapphire Sleet, while other researchers associate related activity with names such as BlueNoroff and Stardust Chollima. An HTTP client looks like an odd target for a crew in that line of work, right up until you think about what compromising one gets you.

Developer machines and build agents, at scale – and those boxes hold cloud role credentials, registry tokens, signing keys, and source for things that haven’t shipped yet. Going after developer tooling instead of production directly is a pattern that runs through earlier npm campaigns too, and it works because developer workstations and build agents hold credentials with unusually broad downstream access.

Verizon’s 2026 Data Breach Investigations Report found that vulnerability exploitation has overtaken stolen credentials as the leading initial-access vector, accounting for 31% of breaches compared with 13% for credential abuse. Credentials didn’t stop mattering, though. They remain one of the most common ingredients once an intrusion is under way, and a compromise that scrapes tokens out of thousands of build environments is exactly the kind of thing that keeps that supply topped up.

Shai-Hulud and the Shift to Self-Propagating Malware

axios was a single publish. The worm campaigns are a different animal entirely.

Shai-Hulud set the pattern in September 2025 with malicious code that, once it’s running in a dev or CI environment, hunts for secrets, finds npm publish tokens, and uses them to push itself into more packages. A related campaign known as Mini Shai-Hulud began on April 29, 2026, and carried on through two more waves in May. Partway through, TeamPCP published the worm’s source code on GitHub, which is not the sort of genie that goes back in the bottle.

The mechanics matter more than the branding. A self-propagating campaign turns every victim with usable publish rights into another distribution point, so the number of affected packages keeps growing without the attacker lifting another finger.

Watch Out: Secret scanning cuts both ways. Several worm campaigns bundled off-the-shelf secret scanners such as TruffleHog to sweep infected hosts for credentials, which is the same tooling defenders run, just pointed the other direction.

That growth curve is the argument for control points that don’t depend on already knowing which packages are bad. Screening packages before they resolve, and checking published artifacts against verified source, can catch whole classes of compromise without waiting for an advisory to exist. That’s the reasoning behind SecureChain for npm.

Mastra and jscrambler Kept the Pattern Going

Two later incidents in 2026 made it clear March wasn’t a one-off. The Mastra AI package takeover on June 17 and the jscrambler compromise on July 11 both came in through publish-side access rather than a vulnerability sitting in someone’s consuming code.

Microsoft attributed the Mastra compromise to Sapphire Sleet, its name for the activity Google tracks as UNC1069. About eleven weeks after axios, the same cluster was back with a very similar playbook. A former contributor account that still held publish rights across the @mastra scope was used to republish more than 140 packages, and the only change in each one was a single added dependency called easy-day-js, a dayjs lookalike carrying a malicious postinstall hook. The library code was never touched, which is exactly what happened with axios.

Sonatype reported more than 454,600 new malicious open source packages across npm, PyPI, and other registries in 2025 alone, so the volume isn’t new. What the recent run shows is how fast compromised publishing access turns that volume into something that reaches a build.

What Would Have Actually Stopped It?

Here are six candidate controls, checked honestly against a three-hour publish-side compromise.

Control What it does Would it have caught axios
npm audit Matches your tree against known advisories No, there was no advisory to match when the installs began
–ignore-scripts Blocks lifecycle script execution Yes, the payload ran from a postinstall hook
Committed lockfile with npm ci Installs the versions already resolved, nothing newer Yes, until the lockfile gets regenerated
Provenance verification Flags artifacts that didn’t come from the expected workflow Likely, where provenance was expected and enforced
Quarantine window on new releases Delays adoption of fresh versions Yes, a 24-hour quarantine would have outlasted the window
Registry screening with rebuild from source Can detect or block published artifacts that diverge from verified source (the approach behind SecureChain for npm) Yes, where the source-integrity checks rejected the divergent artifact and installs resolved through the curated registry

 

–ignore-scripts earned its spot, and the ecosystem has since caught up with it. npm 12 shipped on July 8, 2026, with dependency install scripts off by default, including the implicit node-gyp rebuild that a bare binding.gyp used to trigger, so npm 12 blocks this payload without anyone remembering a flag. Dependencies that genuinely need install-time or native-build scripts now have to be approved explicitly, which is worth working through deliberately before an upgrade quietly breaks a pipeline.

It still isn’t a general fix – malicious code sitting in the module body runs the moment something imports the package, script blocking or not. The July jscrambler compromise is the cleanest illustration of that. The first malicious releases ran from a preinstall hook, and then, inside the same three-hour publishing run, the attacker moved the identical dropper into the package’s own code so it fired when the package was imported or its CLI was run. Blocking install scripts is necessary. It isn’t sufficient.

Lockfile discipline needs a bigger caveat, because it’s the control most teams assume they already have covered.

Watch Out: A caret range like ^1.7.0 in package.json will happily resolve to a malicious patch release when nothing pins it. A committed lockfile prevents that, and npm ci is the stricter way to install from one, since it refuses to run when the lockfile and package.json disagree instead of quietly changing resolution. The protection disappears the moment the lockfile is regenerated or bumped to a version that includes the compromise. Plenty of pipelines miss at least one of those.

Provenance is useful here, and against this specific failure mode it earns its keep.

Did You Know: npm supports Sigstore-based provenance attestation for packages published from CI, and you can check what you already have with one command: npm audit signatures.

Worth being precise about what that proves. Provenance verifies that an artifact came from the source and build workflow it claims, which is a different and far more answerable question than whether the code inside is malicious. For axios it would have helped, because the last clean 1.x release carried provenance and the malicious one didn’t, having been pushed by hand with a stolen token instead of the project’s CI workflow. It is not a malware control, though. The 2026 Mini Shai-Hulud waves shipped malicious packages with valid provenance, because the pipeline that built them really had been compromised.

What to Change Before the Next One?

None of this needs a platform rebuild. Pick based on where your pipeline already sits.

If your lockfiles are inconsistently committed, fix that first and switch CI over to npm ci. It’s one of the simplest controls on the list, and it would have stopped this particular incident outright.

If you’re already running npm ci everywhere, add a quarantine window on new releases. npm has shipped a native min-release-age setting since 11.10.0, pnpm 11 turns minimumReleaseAge on at one day by default although it won’t fail an install unless you set the value yourself, and Unit 42 recommends holding back anything published in the previous 24 to 72 hours on the grounds that most malicious packages get identified and removed inside that window. It wouldn’t have caught everything, since the original Shai-Hulud worm took days to fully clean up, but it covers the common case.

If you publish packages yourself, turn on provenance and move to trusted publishing with OIDC rather than long-lived publish tokens. The axios attacker sidestepped the project’s normal OIDC publishing workflow by using a stolen classic token, which is a reasonable argument for retiring those tokens entirely. You’re part of someone else’s supply chain whether you think about it that way or not.

And if your build agents hold cloud role credentials, shorten those lifetimes as a priority. That’s the actual difference between a bad install and an incident report.

The uncomfortable part is that many of the strongest preventive controls sit before or at resolution, while most of what teams have already invested in is aimed at spotting problems afterwards. That’s not a knock on software composition analysis, which does the job it was built for. It’s a timing problem, and timing problems don’t get fixed by bolting on another scan.

Your mileage will vary depending on how fast the org actually needs new dependency versions. Teams shipping AI tooling against fast-moving upstream packages pay a real cost for quarantine windows. Teams on slower-moving internal platforms pay much less of it, and often find out they were already three weeks behind on minor versions and nobody had noticed.

The Next One Is Already Live Somewhere

Three hours doesn’t feel like a long time until you count the installs that happened inside it. That’s the actual lesson here, not the North Korean attribution or the worm branding. It’s the gap between publish and discovery, and how little sits inside it by default.

axios got noticed. Smaller compromises tend to get a lot less attention. They come off the registry with far less noise, and whoever already installed them is left to work out their own exposure. The next one, sitting four levels deep in somebody’s dependency tree, isn’t going to announce itself either.

Advisory-driven scanning is at its best once a threat is already known. Catching an npm supply chain attack while it’s still happening means screening at install rather than waiting on advisory data to name the package first. That’s the gap SecureChain for npm is built to close.

FAQs

What is an npm supply chain attack?

An npm supply chain attack is a compromise of a package in the registry before it ever reaches you, usually through stolen publish credentials, a hijacked maintainer account, or namespace impersonation rather than a flaw in your own code.

What happened with the axios compromise?

Two malicious versions of axios, 1.14.1 and 0.30.4, went out on March 31, 2026, and were pulled roughly three hours later. Both injected a dependency called plain-crypto-js that dropped a cross-platform remote access trojan from a postinstall hook. Google’s Threat Intelligence Group attributed the compromise to UNC1069, and Microsoft tracks overlapping activity as Sapphire Sleet. The clean versions to fall back to are 1.14.0 and 0.30.3.

Does npm audit detect malicious packages?

Its vulnerability checks depend on advisory data from the registry, so they can’t warn you about a freshly compromised release until that release has been identified and recorded. With axios, an advisory was filed inside the three-hour window, but it couldn’t undo the installs that had already happened. npm audit signatures is a separate check and a more useful one here, since it verifies registry signatures and provenance attestations.

Does a lockfile protect against a hijacked npm release?

A committed lockfile stops an unexpected version from being resolved, and npm ci is the stricter way to install from it, since it refuses to proceed when the lockfile and package.json disagree. That protection goes away when the lockfile is regenerated or bumped to a version that includes the compromise, and npm install is looser because it can change resolution and rewrite the lockfile.

What is the Shai-Hulud worm?

A self-propagating npm malware campaign that harvests secrets from infected environments, finds npm publish tokens, and uses them to push itself into more packages, growing the affected set without the attacker doing anything further. Its source code was published openly in May 2026, so copycat waves are part of the picture now.

How do I check whether a package was built by its real maintainers?

Run npm audit signatures to verify registry signatures and, where they exist, provenance attestations. Provenance can confirm that an artifact was produced by the source and CI workflow it claims, which is a different question from whether the code is safe, since a compromised pipeline can produce a properly attested build.

Stop Rogue Releases Before They Resolve

Relying on advisory feeds alone can mean hearing about a malicious release only after it has already reached a build. SecureChain for npm screens packages at resolution and rebuilds them from verified source to block compromised and policy-violating artifacts before they get that far. Take a look at SecureChain for npm, and subscribe to the TuxCare newsletter for weekly open source supply chain updates.

 

Summary
The axios npm Supply Chain Attack and What It Exposed
Article Name
The axios npm Supply Chain Attack and What It Exposed
Description
Two malicious versions of Axios remained live for 3 hours. See what the npm supply chain attack exposed and which controls actually stop it.
Author
Publisher Name
TuxCare
Publisher Logo

Join 4,500+ Linux & Open Source Professionals!

2x a month. No spam.

Table of Contents
Get the open-source security answers you need

Join 4,500+ Linux & Open Source Professionals!

2x a month. No spam.