The ua-parser-js incident of October 2021 was a textbook example of how a single compromised maintainer account on npm can deliver malware to tens of millions of installations within a single afternoon. The package was ubiquitous, the maintainer was responsive, and the attack window was short, but the spread was nontrivial and CISA issued an advisory the same week.
This deep dive walks through the timeline, the payloads, and the maintainer hygiene story that the incident exposed.
What versions were compromised and for how long?
On October 22, 2021, between approximately 12:15 and 15:20 UTC, the attacker published three malicious versions of ua-parser-js to the npm registry: 0.7.29, 0.8.0, and 1.0.0. The package had roughly seven to nine million weekly downloads at the time and was a transitive dependency of major consumers including Facebook, Microsoft, Amazon, IBM, and Reddit. The legitimate maintainer, Faisal Salman, noticed unusual GitHub activity on his account and the unexpected npm publishes, regained control within hours, and published clean versions 0.7.30, 0.8.1, and 1.0.1 along with a public advisory. CVE-2021-43617 was issued for the incident. CISA published advisory AA21-295A on October 22, recommending immediate audit of any system that had installed the affected versions during the window.
What did the malicious payloads do?
The attacker installed two distinct payloads conditional on the host operating system. The preinstall script first ran a shell script that detected platform. On Linux hosts, it downloaded and executed a binary called jsextension that was a fork of the XMRig Monero miner, mining to wallet address 46HMm6S6kVxbcQRtJtg2NV9c3qe6mZBmrJxn1g7KSF4u3sJX. On Windows hosts, it dropped two files: jsextension.exe, which was also a Monero miner, and a second binary called create.dll that was a password and credential stealer derived from the DanaBot family. The Windows stealer extracted browser-stored passwords, cookies, and discord tokens, transmitting them to a C2 infrastructure that overlapped with previously documented Eastern European cybercrime activity.
The attack had clear financial motivation rather than nation-state strategic intent, but the mechanism of delivery, a compromised maintainer account on a foundational package, would have been equally usable for a more targeted intrusion.
How was the maintainer account compromised?
The account compromise vector was not fully confirmed, but the maintainer publicly stated that his email account had been the entry point and the attacker had used it to reset the npm credentials. Two-factor authentication on npm publishing accounts was not yet enforced at the time. In May 2022, partly in response to this incident and the broader pattern, npm announced mandatory two-factor authentication for the maintainers of the top 500 packages by download volume, expanded later to broader cohorts. The mandatory 2FA rollout closed a meaningful fraction of the attack surface, though it does not address compromise of the underlying source-of-truth GitHub account or successful phishing against a 2FA-enrolled maintainer.
What was the practical blast radius?
The practical blast radius depended on whether an affected version was actually installed during the four-hour window. CI systems with floating version specifiers, like ^0.7.0 or ~0.8.0, that ran builds during the window installed the malicious package and executed the payload. Estimates from npm download metrics suggest somewhere between 100,000 and 300,000 individual installations of the malicious versions during the active period. Confirmed downstream compromises were modest in number, but a significant fraction of affected organizations still completed full credential rotation across their developer machines as a precaution, especially where build agents ran on Windows.
The harder cost was the audit and rotation effort. A typical mid-size engineering organization required between 200 and 500 hours of incident response work to determine exposure, rotate credentials, and update tooling, even when no confirmed malicious activity was observed downstream.
What permanent changes did the incident drive?
The ua-parser-js incident, in combination with the contemporary coa and rc compromises that occurred within weeks of it, drove three permanent ecosystem changes. First, npm accelerated its 2FA rollout and added the npm provenance feature that lets consumers verify a package was built in a known CI environment with attestation. Second, downstream platforms like GitHub Dependabot and Snyk added much more aggressive maintainer-account-compromise heuristics, flagging packages that diverge from their historical publish patterns. Third, the largest consumers, particularly Meta, Microsoft, and Google, accelerated investment in internal package mirrors and pinning policies that decouple their production builds from real-time npm registry state.
By 2026, a top-500 npm package being compromised via maintainer account takeover is still a recurring event, but the median time to detection has dropped from hours to minutes and the median consumer blast radius has dropped considerably.
How Safeguard Helps
Safeguard ingests npm provenance attestations and treats unprovenanced versions of historically provenanced packages as a high-priority signal, often the earliest indicator of a maintainer account compromise. Griffin AI correlates anomalous publish patterns, like a new version with a preinstall script in a package that has never previously had one, against known IOC databases and flags them within minutes of registry publication. Reachability analysis tells you whether the affected code path runs in any of your deployed services, so you can size the response appropriately. Policy gates can quarantine new releases of high-value transitive dependencies for a configurable trust window, eliminating the four-hour exposure pattern that ua-parser-js exploited.