A comprehensive breakdown of the North Korean-linked attack that weaponized one of JavaScript’s most trusted libraries, and the architecture that makes it impossible to repeat.
What Axios Is and Why Attackers Chose It
Axios is a promise-based HTTP client for JavaScript. It handles the low-level networking code that lets applications talk to servers: fetching data, submitting forms, polling APIs. Developers reach for it because it works cleanly in both browser and Node.js environments, and because it abstracts away the complexity of raw XMLHttpRequest and the Fetch API.
The numbers tell you why it was worth targeting. Axios records more than 100 million weekly downloads on npm,¹ while Wiz estimates it is present in roughly 80% of cloud and code environments.² It sits inside React apps, Vue apps, Electron desktop software, mobile apps built with React Native, and countless backend Node.js services. Even if you have never installed it yourself, the SaaS tools you rely on very likely have.
By the Numbers: 100M+ weekly downloads · 80% of cloud environments · Present in React, Vue, Angular, Node.js, Electron, React Native · Used by enterprises across every industry
Attacking axios is not like attacking an obscure library. It is like poisoning a water main. The reach is immediate, the victims are distributed, and many of them never know they were downstream of it.
The Attack: A Minute-by-Minute Breakdown
Microsoft Threat Intelligence has attributed this attack to Sapphire Sleet, a North Korean state-sponsored threat actor.³ The sophistication of the operation reflects that attribution. This was planned, staged carefully, and designed to erase its own tracks.
The Setup: Staging the Weapon 18 Hours Early
At 23:59 UTC on March 30, 2026, roughly 18 hours before the attack became public, the attacker published plain-crypto-js@4.2.0 to the npm registry. This version was clean. Its sole purpose was to establish a publication history, making the package look like a legitimate cryptography library with a track record. The name was chosen to blend in with the dozens of similar-sounding utility packages developers see every day.
One minute later, the attacker published plain-crypto-js@4.2.1. That version contained the obfuscated postinstall script that would become the delivery vehicle for the RAT.
Credential Theft: A Weeks-Long Social Engineering Operation
The post-mortem has since been published (axios/axios issue #10636) and corroborated across SANS, Huntress, Microsoft, and the axios team itself. What happened to Jason Saayman, the primary maintainer of the axios project, was not carelessness. It was a sophisticated, multi-week operation that followed a playbook Sapphire Sleet has been running successfully for years.
The attack started roughly 2 weeks before the malicious publish. The attacker impersonated a real company founder, built a convincing fake Slack workspace, and lured Saayman into a Microsoft Teams call populated with multiple fake participants. When a technically plausible error surfaced during the call, the attacker attempted to get him to install a native app or run a curl command. By that point, his GitHub account had already been compromised. The npm credentials followed.
Understanding the sophistication of this operation matters for how you frame your own risk. This was not a case of a weak password or a developer who skipped MFA. It was a carefully staged identity deception campaign targeting a trusted maintainer with full context of who he was and what access he held. The axios project had modern security controls in place. Those controls did not fail because they were poorly configured. They failed because the attacker bypassed the human layer entirely.
The key technical detail uncovered by Huntress researchers: even on the v1.x branch where OIDC Trusted Publishing was configured, the axios publish workflow still passed NPM_TOKEN as an environment variable alongside OIDC credentials. When both are present, npm uses the token. This meant the long-lived access token was effectively the authentication method for all publishes, bypassing OIDC protections entirely. One stolen token, obtained through social engineering rather than a technical exploit, overrode the entire modern publishing security stack.
The Publish: Two Poisoned Versions in 39 Minutes
Using the stolen token, the attacker changed the maintainer email from jasonsaayman@gmail.com to ifstap@proton.me, a clear indicator of unauthorized access, then published 2 backdoored versions:
- axios@1.14.1, published at 00:21 UTC March 31, tagged latest (meaning a default npm install axios would resolve here)
- axios@0.30.4, published at 01:00 UTC March 31, tagged legacy (covering projects still on the 0.x branch)
Both the current and legacy release channels were poisoned within 39 minutes. The attacker maximized coverage across every project regardless of which version of the axios API they were using.
What Made This Invisible on GitHub: Neither axios@1.14.1 nor axios@0.30.4 exists in the axios GitHub repository. There are no git tags, no commits, no code changes. Nothing. The attack happened entirely at the npm registry layer. Any developer checking GitHub to verify a release was legitimate would have found nothing wrong. The packages look clean in version control because they were never in version control.
The Payload Delivery: Zero User Interaction Required
Neither malicious axios version contained any malicious code directly. Instead, both introduced a single new dependency: plain-crypto-js@^4.2.1. This is the attack’s central insight. By hiding the malware in a transitive dependency, the attacker kept the axios package itself clean, passing any diff-based review, while still ensuring execution.
The plain-crypto-js@4.2.1 package declared a postinstall lifecycle hook pointing to setup.js. npm’s architecture automatically executes postinstall scripts during npm install with no user interaction, no confirmation prompt, and no warning. Simply running npm install axios pulled in plain-crypto-js, which immediately executed its malicious payload.
The dropper used multiple layers of obfuscation: string reconstruction at runtime, platform-specific branching logic, and anti-forensic cleanup. After deploying the RAT, setup.js deleted itself and replaced plain-crypto-js’s package.json with a clean decoy, one with no postinstall script and no setup.js reference. A developer who ran npm audit or inspected node_modules afterward would find nothing. The attack had already succeeded and erased its own evidence.
The Payload: What the RAT Could Do Once Inside
The second-stage payload was a fully featured Remote Access Trojan. Not a credential stealer, not a cryptominer. A RAT. The distinction matters. Credential stealers exfiltrate what they find and leave. A RAT gives the attacker an ongoing, interactive connection to the compromised machine.
Elastic Security Labs reverse-engineered all 3 platform-specific variants and found they shared an identical command set, C2 protocol, and beacon cadence, written in different languages for each operating system:⁵
- macOS: C++ binary, installed at /Library/Caches/com.apple.act.mond
- Windows: PowerShell, disguised as a copy of PowerShell at %PROGRAMDATA%\wt.exe
- Linux: Python script
Every variant could:
- Execute arbitrary shell commands on the compromised machine
- Enumerate user directories, filesystem drive roots, and running processes
- Push new programs to the machine for execution
- Exfiltrate files and directory listings to the attacker’s C2 server
- Maintain persistent access through beacon-style check-ins
C2 Infrastructure Detail: The RAT beaconed to sfrclak.com on port 8000, with POST bodies designed to mimic legitimate npm registry traffic using the npm.org domain prefix as a SIEM evasion technique. The User-Agent string was an Internet Explorer 8 / Windows XP string (mozilla/4.0 compatible; msie 8.0), which is highly anomalous in 2026 and a reliable detection signal. The C2 path /6202033 is the date of the attack reversed: 3-30-2026.
For a developer who ran npm install on the afternoon of March 30, the likely result was an attacker-controlled connection to their machine sitting quietly in the background. Every cloud credential in their environment variables, every SSH key on their filesystem, every API token stored locally: all of it accessible. Silently. For as long as the attacker wanted.
Huntress SOC analysts confirmed 135 results across all operating systems contacting sfrclak.com during their initial investigation, before the public alarm had even been raised.⁶
Watch: Sonatype’s Explanation of the Breach
Sonatype researchers identified the malicious packages within minutes of publication and produced a detailed video walkthrough of the attack. It covers how the compromised maintainer account was used, how the staged dependency was designed to evade detection, and what the postinstall hook execution chain looked like in practice.
Sonatype Video: Axios Was Compromised. Here’s What Happened. “Attackers have figured out they don’t need to compromise the code people trust if they can compromise the trust around it. The malicious capability was introduced through a staged dependency and designed to erase its own tracks, which made the attack harder to spot and slower to understand. That’s not just malware. It shows a more deliberate and mature playbook.” Ilkka Turunen, Field CTO, Sonatype
The Structural Problem npm Cannot Fix
Once you have completed the immediate remediation, it is worth pausing on how this happened, because the structural conditions that enabled it have not changed.
The npm registry was designed for accessibility and speed. Any account holder can publish a package version in seconds. There is no mandatory code review. There is no provenance verification at the point of publication for most packages. The registry’s primary trust mechanism is the maintainer account, and a maintainer account is only as secure as the credentials protecting it.
The axios attack succeeded despite the axios project doing many things right. OIDC Trusted Publishing was configured on the v1.x branch. The maintainer had MFA enabled. The problem was architectural: even with OIDC configured, a long-lived NPM_TOKEN in the CI environment variables provided a parallel path to publication, and that token was what the attacker used.
The Trust Model Problem: When you run npm install, you are implicitly trusting every maintainer account in your entire dependency tree, every account’s credentials remaining uncompromised, every maintainer’s publishing pipeline remaining secure, every transitive dependency you never explicitly chose, and npm’s architecture executing lifecycle scripts without confirmation. The axios attack required compromising exactly 1 account. The rest of that trust chain was irrelevant.
This is not a criticism of the axios maintainers. They responded quickly and correctly. The vulnerability is in the model itself. The same attack has worked before. Against ua-parser-js in 2021, one hijacked account with nearly 8 million weekly downloads resulted in a cryptominer deployed overnight. It will work again on the next high-value target. In Q1 2026 alone, Sonatype identified 21,764 malicious open source packages, 1 new malicious package every 6 minutes, with npm accounting for 75% of them.⁷
What makes the axios compromise harder to dismiss as a one-off is that it was not one. Axios was the 5th npm library targeted in March 2026 as part of a coordinated campaign researchers have since identified as TeamPCP [Source needed]. The same playbook, the same attacker infrastructure, the same technique of staging a dependency before poisoning a trusted package, ran 5 times in a single month. Axios was the one that broke into headlines because of its scale. The others did not need to. The campaign was already working before anyone was looking for it.
That context changes how you should read your own exposure. The question is not whether a sophisticated attacker could social engineer one of your transitive dependency maintainers. The axios post-mortem confirms it is operationally viable against a security-conscious maintainer with MFA enabled. The question is whether your sourcing architecture requires that trust to hold.
Why Your Scanner Will Miss the Next One Too
The post-incident narrative almost always centers on detection: how fast the malware was spotted, which scanner flagged it, and why your tool did or did not catch it. That framing understates the fundamental problem.
The axios attack was designed specifically to defeat post-install inspection. The malicious code executed and erased itself. Any developer who ran npm audit or inspected their node_modules folder after the fact found no postinstall script, no setup.js, and no indication that anything had happened. The manifest had been replaced with a clean decoy.
Malwarebytes on the Cleanup: “Any post-infection inspection of node_modules/plain-crypto-js/package.json will show a completely clean manifest. There is no postinstall script, no setup.js file, and no indication that anything malicious was ever installed. Running npm audit or manually reviewing the installed package directory will not reveal the compromise.”⁸
This is the core failure mode of a scan-and-pray security posture: scan for problems after installation, pray you catch them before they run. The axios attack was designed to complete execution before any post-install scan could observe anything. The attack had won before your scanner even ran.
Even dependency cooldowns, which are time-based filters that delay pulling newly published package versions for some number of days, do not address this category of attack. A 7-day cooldown assumes the community will detect and report the malicious version within that window. The axios attack was live for approximately 3 hours before being flagged. A 12-hour cooldown would have stopped it. But the attacker knew the tooling landscape. A future attack can be designed to survive any cooldown window by delaying payload execution. Once cooldowns are widespread, patient attackers adapt their timelines. The window is not a guarantee.
The Regulatory and Personal Liability Dimension
The technical remediation is the immediate problem. The strategic one is the question that comes next, when the security leader is sitting across from general counsel, an audit committee, or an SEC examiner.
The question is not “why did your scanner miss it?” Scanners miss things. Everyone knows that. The question is: “Why did unvetted open source reach your developers’ machines in the first place?” In 2026, that question has a regulatory dimension.
The EU Cyber Resilience Act, the SEC’s cybersecurity disclosure rules, and emerging software supply chain executive orders in the US have all moved in the same direction: the burden of proof is on the organization to demonstrate that software was secure at the point of origin, not merely that it was scanned at some point after arrival. Reactive detection is no longer sufficient as a legal posture.
The Liability Shift: “In the 2026 regulatory environment, the burden of proof has shifted. Security leaders are expected to demonstrate not just that they scanned for vulnerabilities, but that their software supply chain was governed — that policy was applied at the source, that components were admitted by policy, not by default.” Abby Kearns, CEO, ActiveState
The developers who ran npm install on March 30 very likely had SSH keys, cloud credentials, API tokens, and production system access on their machines. The RAT that axios delivered had arbitrary command execution and persistent access. If those credentials were exfiltrated before the attack was detected, the question becomes: Did the organization have the governance controls in place that regulators expect? Scanning after the fact is not the same as governed sourcing.
How an ActiveState Curated Catalog Stops This Attack
The fundamental difference between a scan-and-pray posture and a curate-and-govern posture is where the security decision happens. Scan and pray asks: did anything bad slip through? Curate and govern asks: what conditions must be true for something to enter the environment at all?
The ActiveState Curated Catalog is built on the second question. Here is how each element of the axios attack maps against it.
The Attack Used a Compromised npm Account: Curated Catalog Blocks at the Source
The ActiveState Curated Catalog does not pull open source components as-is from npm. Every component is built from verified source code in an SLSA Level 3 compliant build environment. SLSA (Supply chain Levels for Software Artifacts) Level 3 requires hermetic, reproducible builds with cryptographically signed provenance attached to every artifact. That provenance covers not just the final binary, but every step in the build chain.
A component published to npm via a stolen token, bypassing the CI/CD provenance chain, would produce a binary without matching SLSA provenance. That provenance mismatch is a hard admission failure. The component does not enter the catalog. It does not reach your developers. The attack stops before it starts.
The Attack Injected a Malicious Transitive Dependency: Curated Catalog Governs the Full Tree
The malicious payload was in plain-crypto-js, not in axios itself. If you had inspected axios source code, you would have found nothing wrong. The attack relied on the fact that most teams govern their direct dependencies but have limited visibility into the transitive tree.
The ActiveState Curated Catalog tracks and governs every open source component in the full dependency tree. This includes not just direct dependencies but every transitive dependency a cataloged component pulls in, each subject to the same CVE scanning, malware analysis, and provenance verification. There is no ungoverned subtree. No layer of the dependency tree exists outside the security policy.
In the axios attack, plain-crypto-js would have been evaluated as a new dependency before being admitted. A newly published package with an obfuscated postinstall hook and no publication history consistent with a legitimate cryptography library would not pass that evaluation.
The Attack Used a Postinstall Hook for Silent Execution: Curated Catalog Eliminates Arbitrary Script Execution
npm’s postinstall lifecycle hook is the mechanism that made the axios attack automatic. Simply running npm install triggered the malicious code. No user interaction. No warning.
Open source components delivered through the ActiveState Curated Catalog are built from source by ActiveState. The arbitrary postinstall scripts declared by package maintainers do not execute in your environment. The build runs in ActiveState’s SLSA Level 3 infrastructure. What arrives in your developers’ environments is a verified binary artifact, not a package with active lifecycle hooks waiting to fire.
The Attack Self-Erased: Curated Catalog’s Protection Is Pre-Execution
The axios attack was specifically designed to defeat post-installation inspection. It succeeded. By the time any scan could run, the dropper had deleted itself and replaced its package.json with a clean file.
The Curated Catalog’s protection is not inspection after installation. It is proactive admission control before anything reaches the developer. If plain-crypto-js@4.2.1 had never been admitted to the catalog (because its obfuscated postinstall hook failed malware analysis, because its provenance was unverifiable, because it was a brand-new package with no track record), then no amount of clever cleanup code matters. The question of whether it self-erases is irrelevant because it never ran.
The Attack Propagated Through Transitive Dependencies Across Unrelated Packages: Curated Catalog Provides Continuous Monitoring
Huntress researchers confirmed the attack was broader than axios alone: plain-crypto-js appeared in packages associated with OpenClaw, in Datadog packages, and even inside a WordPress module nested at C:\Users\[REDACTED]\AppData\Roaming\npm\node_modules\@wordpress\scripts\node_modules\plain-crypto-js\.⁶
Any package that transitively depended on axios, in any version, was potentially affected. This is the definition of uncurated transitive dependency exposure. The attack propagated through dependencies that the affected teams had never seen, never chosen, and had no governance over.
The Curated Catalog’s continuous monitoring means that when an open source component like plain-crypto-js is flagged as malicious anywhere in the ecosystem, every catalog that contains it receives an alert. Security teams do not find out by reading a blog post. They are notified at the source, with the affected components identified, before developers have an opportunity to install a compromised version.
Every attack vector that made the axios compromise possible (the compromised account, the hidden transitive dependency, the postinstall hook, the self-erasing cleanup) is addressed by the architecture of the Curated Catalog, not by inspection after the fact.
axios@1.14.1 and axios@0.30.4. Both were published on March 31, 2026, and removed from the npm registry at approximately 03:15 UTC the same day. Safe versions are axios@1.14.0 (the last legitimate 1.x release with SLSA provenance) and axios@0.30.3 (the last legitimate 0.30.x release).
If your lockfile pinned axios to a version earlier than 1.14.1 or 0.30.4, and if you ran npm ci without any update step, you were likely not directly affected by the axios versions. However, if any of your dependencies have a loose range that resolved to a package pulling plain-crypto-js as a transitive dependency, the lockfile protection does not extend there. The Huntress investigation confirmed the attack propagated through packages unrelated to axios that happened to include it transitively.
You cannot know for certain through post-installation inspection alone. The dropper was specifically designed to clean up after itself: it deleted setup.js and replaced package.json with a clean decoy. Running npm audit or checking node_modules will show nothing. The only reliable indicators are network logs showing connections to sfrclak.com or 142.11.206.73, or evidence of the RAT binaries (/Library/Caches/com.apple.act.mond on macOS, %PROGRAMDATA%\wt.exe on Windows). If you cannot rule out the possibility that a compromised version was installed, rotate credentials and treat the machine as potentially compromised.
Microsoft Threat Intelligence has attributed the attack to Sapphire Sleet, a North Korean state-sponsored threat actor.³ Google Threat Intelligence Group confirmed it as a separate incident from another major npm software supply chain attack disclosed the same week, and warned the incident could have “far-reaching impacts” given axios’s presence in roughly 80% of cloud environments.
The attack had 4 key elements: a compromised npm account used to publish without going through the normal OIDC-based CI/CD flow (no matching provenance); a malicious transitive dependency (plain-crypto-js) introduced through the axios manifest; an automatic postinstall hook that executed malicious code on installation; and a self-erasing cleanup to defeat post-installation inspection.
The Curated Catalog addresses all 4. Open source components are built from verified sources in SLSA Level 3 infrastructure, so a package published via a stolen token without matching provenance would not be admitted. Every transitive dependency is scanned before admission, meaning plain-crypto-js with its obfuscated postinstall hook would fail malware analysis. Components are delivered as verified binary artifacts without arbitrary postinstall execution in developer environments. And because the protection is pre-admission rather than post-installation, the self-erasing cleanup is irrelevant. The component never reached the developer.
Yes. Every open source component in the ActiveState Curated Catalog, including every transitive dependency, is scanned for known CVEs and for malware indicators before admission. The 2 are distinct analyses: CVE scanning identifies known vulnerabilities against public databases; malware analysis looks for behavioral indicators like obfuscated code, suspicious network calls, unusual filesystem writes, and postinstall hooks with encoded payloads. The axios attack would have been flagged primarily by malware analysis, since plain-crypto-js@4.2.1 had no CVE at the time it was published. It was purpose-built malware, not a vulnerability in existing code.
Those controls help and are worth keeping. Pinning direct dependencies reduces your exposure to version bumps that could resolve to malicious versions. Dependabot surfaces known vulnerabilities in your dependency tree. But neither addresses the underlying trust model: you are still pulling from npm, which means you are trusting the authentication integrity of every maintainer account in your transitive dependency tree. Both Dependabot and pinning would have been ineffective against the axios attack for teams that had previously installed axios and whose cached version was updated by a dependency resolution.
In the short term: adopt dependency cooldowns (7 days minimum, 12 hours at a minimum for production-critical paths), pin all direct dependencies, and audit your wide semantic version ranges.
In the medium term, audit who governs your full dependency tree, not just your direct dependencies. Determine where policy is applied: at the source, or downstream in CI logs after the fact. Evaluate whether your AI-assisted development workflows are generating dependency declarations outside your governed sourcing pipeline.
In the longer term, a sourcing model that applies governance at the point of admission, not detection at the point of failure, is the only architecture that is structurally robust against the attack pattern that axios demonstrated. The question to answer is not “how fast can we detect the next one?” It is “what would have to be true for the next one to never reach our build at all?”
ActiveState provides the world’s largest library of secure, built-from-source open source components: 79 million components across 12 language ecosystems, delivered through the tools your teams already use. Every component is scanned for CVEs and malware before admission, built in SLSA Level 3 infrastructure, and continuously monitored for new vulnerabilities with contractual remediation SLAs.