8 Million Downloads a Week, Now Serving Malware
On October 22, 2021, the maintainer of ua-parser-js — one of the most popular npm packages with roughly 8 million weekly downloads — discovered that his account had been hijacked. The attacker published three malicious versions: 0.7.29, 0.8.0, and 1.0.0.
The compromised versions included preinstall scripts that downloaded and executed platform-specific malware. On Linux, it installed a cryptominer. On Windows, it deployed both a cryptominer and a credential-stealing trojan. Every developer and CI system that installed or updated the package during the compromise window was affected.
ua-parser-js is used by major frameworks and platforms including Facebook's fbjs library. Its dependency tree is enormous — a compromise ripples through thousands of downstream projects.
What Happened
The attacker gained access to the maintainer's npm account. How exactly remains unclear — credential stuffing, phishing, or token theft are all plausible vectors. npm accounts at the time did not require two-factor authentication by default, even for packages with millions of downloads.
Once in control, the attacker published versions that appeared to be legitimate updates. The malicious code was in the preinstall script, which executes automatically when npm installs the package. Developers who ran npm install or npm update triggered the payload without any additional interaction.
The Payload
Linux systems received:
- A preinstall script that downloaded a binary from a remote server
- The binary was an XMRig cryptominer configured to mine Monero
- It established persistence through a cron job
Windows systems received:
- A preinstall script that downloaded two executables
- One was an XMRig cryptominer
- The other was a credential stealer that harvested saved passwords from Chrome, Firefox, and other browsers, plus cookies and saved form data
- The stealer exfiltrated data to an attacker-controlled server
Affected Versions
0.7.29— Published alongside the legitimate 0.7.x line0.8.0— A new minor version that didn't previously exist1.0.0— A new major version that didn't previously exist
The 0.7.29 version was particularly dangerous because existing projects with ^0.7.28 in their package.json would automatically install it during updates.
Timeline
- October 22, 2021, ~12:00 UTC — Malicious versions published
- October 22, 2021, ~16:00 UTC — Maintainer noticed the compromise and alerted npm
- October 22, 2021, ~18:00 UTC — npm unpublished the malicious versions
- October 22, 2021 — Clean versions 0.7.30, 0.8.1, and 1.0.1 published
The window was approximately six hours. In that time, the malicious versions were downloaded hundreds of thousands of times.
Why This Matters
Single Maintainer, Millions of Users
ua-parser-js is maintained by a single developer. One person's compromised credentials led to millions of potentially affected installations. This isn't unusual — many critical npm packages are maintained by one or two people with no formal security support.
The npm Trust Model
npm's trust model is built on account security. If an attacker controls a maintainer account, they can publish anything. There's no code review, no multi-party signing, no behavioral analysis of published packages (though npm has since added some checks). The entire security model depends on the maintainer's account not being compromised.
Transitive Dependency Amplification
ua-parser-js is a dependency of thousands of other packages. When it's compromised, every project that depends on it — directly or transitively — is at risk. Most affected developers didn't choose to use ua-parser-js. It was pulled in as a transitive dependency of something else entirely.
Preinstall Scripts Are a Major Risk
npm's preinstall and postinstall scripts are one of the most dangerous features in any package ecosystem. They execute arbitrary code on install with the permissions of the user running npm. They're used legitimately for building native modules and running post-installation setup, but they're also the primary mechanism for npm malware.
Immediate Remediation
If you installed any of the affected versions, the remediation steps were:
- Remove the compromised versions and install clean versions (0.7.30, 0.8.1, or 1.0.1)
- Rotate all credentials that were accessible on affected systems
- Check for unauthorized processes (cryptominers)
- On Windows, assume credentials stored in browsers were stolen — change passwords for all saved accounts
- Scan systems for persistence mechanisms (cron jobs, scheduled tasks)
Preventing Future Incidents
- Use lockfiles —
package-lock.jsonpins exact versions. With lockfiles, automatic minor version updates won't pull in compromised packages. - Review dependencies regularly — Tools like
npm auditcatch known malicious packages after they're flagged. - Disable install scripts in CI — Use
--ignore-scriptswhen possible to prevent preinstall script execution. - Enable npm 2FA — If you maintain packages, enable two-factor authentication. npm now requires 2FA for top packages.
- Monitor for unexpected dependencies — Track your dependency tree and alert on changes.
How Safeguard.sh Helps
Safeguard.sh monitors your dependency tree in real time and alerts immediately when any component is flagged as compromised or malicious. When the ua-parser-js hijack occurred, teams using Safeguard.sh would have been notified within minutes of the advisory, with a clear list of every project in their organization that depended on the affected versions.
The platform maintains a continuously updated database of known-malicious packages across npm, PyPI, and other registries. When a package is compromised, Safeguard.sh cross-references it against your complete dependency tree — including transitive dependencies — to identify your exposure. You don't need to manually check each project; the platform does it automatically across your entire portfolio.
Safeguard.sh also tracks dependency version changes and flags unexpected updates that don't match your lockfile expectations. This behavioral monitoring catches supply chain compromises even before they're publicly disclosed, providing an additional layer of defense beyond vulnerability databases.