← Back to blog

Your Dependencies Are Under Attack: Lessons from xz, polyfill.io, and event-stream

Three real supply-chain attacks — xz utils (CVE-2024-3094), polyfill.io (110K+ sites), and event-stream (cryptocurrency theft) — and what they teach about dependency auditing that actually prevents incidents.

Supply ChainDependenciesCVE-2024-3094npm Security

Key Takeaways

  • CVE-2024-3094 (xz utils) was a CVSS 10.0 backdoor inserted over two years of social engineering — not a code exploit, but a process exploit.
  • The polyfill.io attack compromised 110,000+ websites after a domain acquisition, not a package vulnerability.
  • event-stream's compromise in 2018 targeted a single downstream package (copay) for cryptocurrency theft — showing that attackers choose their targets through dependency graphs.

xz utils: the two-year social engineering campaign (CVE-2024-3094)

On March 29, 2024, a backdoor was discovered in xz utils versions 5.6.0 and 5.6.1 — a compression library included in virtually every Linux distribution. The backdoor allowed remote code execution through OpenSSH by hijacking the authentication path. It received CVSS 10.0.

The attack wasn't a code exploit. An attacker using the identity 'Jia Tan' spent two years building trust as a maintainer — submitting legitimate patches, gaining commit access, then gradually introducing obfuscated malicious code through the build system (not the source repository directly). The backdoor was hidden in test fixture files and activated through a modified build script.

The lesson: your dependency risk isn't limited to known CVEs in source code. It includes maintainer trust, build system integrity, and the difference between what's in the repository and what's in the published artifact. Automated scanners that only check source code would have missed this entirely.

polyfill.io: 110,000 websites compromised via domain acquisition

In February 2024, a company called Funnull acquired the polyfill.io domain and its associated GitHub account. The polyfill.io CDN was used by over 110,000 websites to serve JavaScript polyfills. After acquisition, the new owners modified the served JavaScript to redirect users to malicious and scam sites.

Google blocked ads for affected sites. Cloudflare and Fastly created automatic replacements. But the damage window was weeks — any site loading scripts from the CDN was serving attacker-controlled JavaScript to every visitor.

The lesson: a dependency isn't just a package in your lock file. It's every external resource your application loads at runtime. CDN-hosted scripts, third-party analytics, externally loaded fonts with JavaScript — all of these are dependencies with the same trust implications as npm packages, but without version pinning or integrity checks.

event-stream: targeted cryptocurrency theft through the dependency graph

In 2018, a new maintainer took over the popular event-stream npm package (downloaded 2M+ times/week) after the original author lost interest. The new maintainer added a dependency called flatmap-stream, which contained encrypted malicious code targeting a single downstream package: the Copay Bitcoin wallet.

The attack was surgically targeted. The malicious code only activated when running inside the Copay build process, where it attempted to steal cryptocurrency wallet credentials. For every other consumer of event-stream, the package worked normally.

The lesson: attackers map dependency graphs to find high-value targets reachable through popular packages. Your exposure isn't just 'do my dependencies have CVEs?' — it's 'who else uses my dependencies, and does that make me a stepping stone or a target?'

What these attacks teach about dependency auditing

Traditional dependency auditing — running npm audit and fixing what's red — would have caught none of these three attacks. They require a different model:

  • Track maintainer changes on critical dependencies. A new maintainer on a foundational package is a risk signal, not just a GitHub notification to dismiss.
  • Pin and verify. Use lock files, enable npm's package-lock verification, and consider Subresource Integrity (SRI) for any CDN-loaded scripts.
  • Distinguish runtime from dev-only. A vulnerability in your test framework is categorically different from one in your authentication library. Triage accordingly.
  • Monitor for domain/ownership changes on external resources. CDN URLs, analytics scripts, and third-party widgets are dependencies too.
  • Audit the build, not just the source. xz proved that the published artifact can differ from the repository. Reproducible builds and artifact verification matter.

A practical weekly cadence

Run automated advisory checks on every PR (catches known CVEs in new additions). Weekly: review any new advisories against your lock file, prioritized by runtime reachability. Monthly: check for maintainer changes on your top-20 most critical dependencies. Before major releases: full transitive dependency review with exploitability assessment.

The goal isn't zero advisories — that's impossible for any real project. The goal is knowing which advisories represent actual production risk and having a response time measured in days, not months.