Safeguard
Supply Chain Attacks

Anatomy of the polyfill.io CDN Compromise

In June 2024, a single acquired domain turned a free CDN trusted by over 100,000 sites into a live malware injection point — with no dependency update required.

Safeguard Research Team
Research
6 min read

On June 25, 2024, security researchers at Sansec publicly disclosed that cdn.polyfill.io — a free CDN serving JavaScript compatibility shims to an estimated 100,000-plus websites — had been quietly weaponized to distribute malware. The domain and its GitHub account had changed hands in February 2024, acquired by a company called Funnull, four months before anyone raised an alarm. No dependency file changed. No package version bumped. Sites that had added a single <script src="https://cdn.polyfill.io/v3/polyfill.min.js"></script> tag years earlier, and never touched it again, woke up serving attacker-controlled code to their visitors. Google had already begun flagging Google Ads pointing to affected sites as leading to compromised websites on June 21. Confirmed victims included Hulu and Atlassian's community forum, and the exposure was serious enough that government site usage was reported to CISA. The polyfill service had once handled on the order of tens of millions of daily requests at its peak popularity, which is exactly why a domain-level takeover, rather than a code vulnerability, could reach so far, so fast. This is what that attack looked like mechanically, and what it means for how teams should evaluate third-party script trust going forward.

What actually changed hands, and when?

The original polyfill.io project was created by developer Jake Champion to let older browsers run modern JavaScript by serving polyfills conditionally based on the requesting browser's user-agent. In February 2024, Champion sold the domain and the associated GitHub organization to Funnull, a company researchers have linked to China-based infrastructure. That transfer is the entire attack: no code repository was hacked, no build pipeline was breached, and no CVE was ever assigned, because there was no software vulnerability to patch. The compromise was a change of ownership over a trusted domain that thousands of sites had wired directly into their HTML years earlier, with no mechanism in place to notice or re-approve the change. By the time Sansec published its findings in late June, the malicious infrastructure had reportedly been live for months.

How did the injected code actually behave?

Once under new ownership, cdn.polyfill.io began serving JavaScript that behaved differently depending on the visitor. According to Sansec's analysis, the script inspected headers such as user-agent and referrer and, for a subset of matching visitors — primarily mobile users landing from specific referrers — redirected them to sports betting and adult-content phishing pages, with capability to drop further payloads. Traffic pointed instead at polyfill.io.bsclink.cn via a CNAME pivot, and Sansec flagged a cluster of related domains — including polyfill[.]site, bootcdn[.]net, bootcss[.]com, staticfile[.]net, and unionadjs[.]com — as part of the same infrastructure. Conditioning the payload on user-agent and referrer was deliberate evasion: a security researcher casually loading the script from a desktop browser with no referrer would see nothing malicious, which is exactly why the compromise persisted undetected for months after the February ownership change.

Why did a free CDN reach 100,000+ sites in the first place?

Polyfill.io succeeded specifically because it solved a real, boring problem — browser compatibility — with zero setup cost: paste one script tag, get automatic polyfills. At its height the service reportedly handled tens of millions of requests per day, and sites embedded it directly rather than vendoring the code, because the entire value proposition was "let the CDN handle browser detection so you don't have to." That convenience is precisely what erased any friction that might have triggered re-review: a <script src> tag pointing at an external domain doesn't show up in a dependency scanner, doesn't appear in a package.json diff, and doesn't trigger a Dependabot alert when its remote owner changes. It sits in your HTML, silently trusted, for as long as nobody thinks to look at it again.

How did the industry respond once it was disclosed?

Response was fast once Sansec's disclosure landed on June 25, 2024. Cloudflare and Fastly each stood up free, safe mirror endpoints serving the last known-good polyfill code — Cloudflare's at cdnjs.cloudflare.com/polyfill — so sites could swap the domain without ripping out functionality overnight. jsDelivr and other CDN operators removed or blocked references to polyfill.io outright. Ad-blocking lists, including uBlock Origin's filter lists, added the domain to block malicious script loads at the browser level for users who had them installed. Namecheap, the registrar, later suspended the polyfill.io domain entirely. The overall pattern — infrastructure providers acting as an emergency circuit breaker faster than individual site owners could even audit their own HTML — says as much about the incident as the compromise itself: remediation for a domain-trust attack has to happen upstream, because thousands of independent site owners will not coordinate a fix in real time.

What does this attack expose that dependency scanning doesn't cover?

Standard software composition analysis matches declared dependencies — entries in requirements.txt, package.json, or a lockfile — against vulnerability and malware databases. Polyfill.io was never declared as a dependency in that sense; it was a runtime fetch from a third-party domain referenced directly in markup, invisible to any tool that only inspects your build manifest. That's a structural blind spot: a package registry compromise (malicious versions published to npm or PyPI) gets caught by continuous re-scanning of declared dependencies, but a CDN or script-tag domain takeover changes behavior without touching a single file in your repository. The practical lesson researchers converged on afterward was consistent: self-host third-party scripts where feasible, or pin them with Subresource Integrity (SRI) hashes so a byte-for-byte change at the source breaks the script load instead of executing silently, and treat every externally-sourced <script src> as a supply chain dependency with an owner who can change — because in this case, one did.

How does Safeguard help?

Safeguard's continuous scanning and Eagle malware-classification pipeline are built to catch the supply chain attacks that show up as declared dependencies — a malicious package version published to npm, PyPI, or another registry, or a compromised container image or model artifact — by re-evaluating your SBOM every time new threat intelligence lands, rather than waiting for the next manual scan. That gives teams durable visibility into the packages and images they actually pull into a build. Polyfill.io's specific mechanism — a runtime fetch from a third-party domain embedded directly in HTML rather than a declared dependency — sits outside that scope, which is exactly why the SRI-and-self-hosting discipline above matters as a complementary control: SBOM-driven dependency visibility and browser-level script integrity are two different halves of third-party trust, and this incident is a reminder to cover both.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.