In short

polyfill.io was a free service that sent each browser the JavaScript polyfills it needed. In February 2024 its domain and GitHub account were sold to a company called Funnull; by June the service was injecting code that redirected some mobile visitors to scam and betting sites, on more than 100,000 websites by Sansec's estimate. Because the response changed with each browser, Subresource Integrity could not pin it. Removing the dependency, self-hosting fixed files with SRI, and a strict script-src policy were the controls that worked.

  • The attack needed no compromise: whoever owns a script's domain decides what every embedding site runs.
  • Warnings came early: the original author urged sites to stop using it, and Cloudflare flagged the new ownership as a supply-chain risk on 2024-02-29, four months before the malicious code was reported.
  • SRI pins one exact file, and polyfill.io returned different code per User-Agent, so the people using it could not pin it.
  • The injected code activated only on some mobile devices at some hours and stayed quiet for admins and analytics, which kept it hidden.
  • Namecheap suspended the domain on 2024-06-27; Cloudflare and Fastly offered mirrors, and Cloudflare rewrote links for sites it proxied.
  • For payment pages, PCI DSS v4.0.1 requirements 6.4.3 and 11.6.1 now require an inventory and integrity checks for every script.

Incident at a glance

Dates
to
Category
Supply-chain compromise
Organizations
  • polyfill.io
  • Funnull
  • pdoc
Impact
After the domain's sale, the polyfill.io CDN served code that redirected some mobile visitors to scam and betting sites, on more than 100,000 websites by Sansec's estimate. The code differed per browser, so Subresource Integrity could not protect sites that loaded it.
How it was found
Sansec reported on 2024-06-25 that cdn.polyfill.io was injecting malware; Cloudflare later found indicator matches in its page-script monitoring from 2024-06-08.

Timeline

  1. The polyfill.io domain and GitHub account are sold to Funnull; the service's original author tells sites to remove it.
  2. Cloudflare publishes a mirror on cdnjs and warns of the supply-chain risk of the new ownership.
  3. Cloudflare's page-script monitoring later finds matches for the published indicators from this date.
  4. Sansec reports the malicious injection; Google blocks ads for e-commerce sites that use it; CVE-2024-38526 is published for pdoc.
  5. Cloudflare starts rewriting polyfill.io links on sites it proxies.
  6. Namecheap places the domain on hold.
  7. Sansec links other CDN domains to the same operator.
  8. The US Treasury sanctions Funnull Technology Inc.

Verify it

  1. Search source, templates, CMS settings and built HTML for polyfill.io and the related domains, and remove every reference.
  2. List every third-party script origin your pages load, with an owner and a reason for each.
  3. Check that each remaining external script is a fixed file with an integrity attribute, or self-hosted.
  4. Confirm the Content-Security-Policy script-src names only the origins you listed, with no wildcards; /tools/csp-evaluator/ checks a policy in the browser.
  5. On payment pages, confirm the script inventory and change detection that PCI DSS 6.4.3 and 11.6.1 require are running.
Applies to
  • Sites embedding cdn.polyfill.io or polyfill.io Any reference after the February 2024 sale; malicious responses reported from June 2024
  • pdoc Before 14.5.1, when generating documentation with --math (CVE-2024-38526)

Websites and web applications that load JavaScript from third-party domains at run time, including CDNs, analytics, tag managers and polyfill services, and especially payment pages.

Part of
Workflow: Respond to a vulnerability. Collection: case studies
Learning goals and prerequisitesAbout 27 minutes: 7 to read and 20 for the exercise

What you will learn

  • Explain how a domain sale turned a trusted script tag into a malicious one without any compromise.
  • Explain why SRI could not protect a service that returned different code per browser.
  • Find third-party script references in source, builds and live pages.
  • Apply inventory, pinning, CSP and PCI DSS 6.4.3 and 11.6.1 controls to third-party scripts.

Before you start

Front-end and web platform engineers, and AppSec teams responsible for third-party scripts on websites and payment pages

  • Know how a browser loads and runs a script from a third-party domain.
  • Know what a Content Security Policy and the integrity attribute do.
  • For the code: ripgrep, curl and OpenSSL.

Refresh first: Configure HTTP security headers from a tested baseline

Go to the guided practice

A script tag whose owner changed

Polyfills add modern JavaScript features to older browsers. The polyfill.io service made that easy: a site added one script tag pointing at cdn.polyfill.io, and the service read each visitor's User-Agent header and returned only the polyfills that browser needed. Many sites added the tag years ago and never revisited it.

In February 2024 the domain and the project's GitHub account were sold to Funnull, a company with no track record in running the service. The project's original author publicly told sites to remove it, and Cloudflare warned on 2024-02-29 that sites now depended on a new, unknown operator. On 2024-06-25 Sansec reported that the service was injecting malicious code. Nothing had been hacked: the new owner of the domain served what it chose, and every site that embedded the tag ran it.

Timeline

Timeline
WhenEventSource
February 2024The polyfill.io domain and GitHub account are sold to Funnull; the original author warns sites to remove polyfill.io.Sansec; Cloudflare
2024-02-29Cloudflare publishes a mirror on cdnjs and warns of the supply-chain risk of the new ownership.Cloudflare
From 2024-06-08Cloudflare later finds matches for the published indicators from this date in its page-script monitoring.Cloudflare (2024-06-26)
2024-06-25Sansec reports that the domain injects malware into embedding sites; Google starts blocking ads for e-commerce sites that use it; CVE-2024-38526 is published for pdoc.Sansec; CVE record
2024-06-26Cloudflare starts rewriting polyfill.io links on sites it proxies, on by default for free plans.Cloudflare
2024-06-27Namecheap places the domain on hold.Sansec
2024-06-28Sansec links other domains to the same operator, including bootcdn.net, bootcss.com and staticfile.net.Sansec
2025-05-29The US Treasury sanctions Funnull Technology; its release says that in 2024 Funnull bought a code repository used by web developers and altered it to redirect visitors of legitimate sites, without naming polyfill.io.US Treasury

What did the injected code do?

According to Sansec, the injected code redirected mobile users to a sports betting site through a domain posing as Google Analytics. It was selective by design: it activated only on certain mobile devices and at certain hours, stayed inactive when it found an admin cookie, and delayed itself when it detected a web analytics service, so site owners testing their own pages, and their analytics, were unlikely to see anything.

Redirects to scam sites were what was observed, but the capability was larger. A script loaded into a page runs with the page's full authority: it can read what users type, including card numbers and passwords on checkout and login pages, and send it anywhere the browser allows. That is why the response treated every embedding site as running untrusted code, whatever the payload of the day.

Why couldn't Subresource Integrity protect these sites?

Subresource Integrity (SRI) lets a page say which exact bytes it expects from a script: the integrity attribute carries a hash, and the browser refuses a file that does not match. It is the standard defense against a CDN serving altered code, and it would have blocked this attack for a fixed file.

polyfill.io was not a fixed file. The service built a different bundle for each browser from the User-Agent header, so no single hash could match every visitor, and sites using it could not adopt SRI. That made the risk structural: the only safe options were to stop depending on the service or to replace it with fixed files you control. Many sites no longer needed it at all, because the browsers they support already implement the features it polyfilled.

Find references in code, builds and live pages

Script tags hide in many places: page templates, CMS theme settings, tag managers, documentation generators such as pdoc, and third-party widgets that load further scripts. Search the source and the built output, and check what live pages actually load, because a tag manager or a widget can add a script that appears in neither.

Testfind-polyfill-references.shShell
# Source, templates and built output: references to polyfill.io and the
# other domains Sansec tied to the same operator on 2024-06-28
rg -n --hidden -e 'polyfill\.io' -e 'bootcdn\.net' -e 'bootcss\.com' -e 'staticfile\.net' .

# Every third-party script origin in the built site, with a count
grep -rhoE '<script[^>]+src="https?://[^/"]+' dist/ |
  sed -E 's/.*src="//' | sort | uniq -c | sort -rn

# A live page, as a browser receives it
curl -s https://www.example.com/checkout | grep -oE "https?://[^\"' ]*polyfill\.io[^\"' ]*"

Replace dynamic third-party scripts with pinned ones

Where you keep a third-party library, serve a fixed file, either self-hosted or from a versioned CDN URL, with an integrity hash and crossorigin="anonymous", and restrict where scripts may load from with a Content Security Policy. /domains/appsec/browser-security/security-headers/ and /domains/appsec/browser-security/cors-csp-browser-boundaries/ cover CSP in depth. A strict script-src would not have stopped a site that explicitly allowed polyfill.io, but it stops scripts that a compromised third party pulls in from further domains, and it makes the list of trusted origins explicit and reviewable.

Fixedpin-third-party-script.shShell
# Self-host a fixed file and pin it: compute the SRI hash of the exact bytes you serve
curl -fsSLo vendor/lib.min.js https://cdnjs.cloudflare.com/ajax/libs/lib/1.2.3/lib.min.js
echo "sha384-$(openssl dgst -sha384 -binary vendor/lib.min.js | openssl base64 -A)"

# Reference it with the hash; the browser refuses the file if a single byte changes:
#   <script src="/vendor/lib.min.js"
#           integrity="sha384-<hash printed above>" crossorigin="anonymous"></script>

# And allow scripts only from origins you control:
#   Content-Security-Policy: script-src 'self'; object-src 'none'; base-uri 'none'

Which controls would have helped?

A script tag is a dependency like any package, but it is resolved by every visitor's browser at run time, with no lockfile and no review. The controls bring it back under the same discipline.

Which controls would have helped?
ControlPracticeEffectGuide
Inventory every third-party script with an owner and a reasonSSDF PW.4 (reuse well-secured software); PCI DSS 6.4.3The February warning would have reached someone who knew polyfill.io was in use./frameworks/pci-dss-requirement-6/
Remove scripts you no longer needSSDF PW.4Most sites did not need polyfills for their supported browsers./domains/supply-chain/dependencies/project-health-and-intake/
Self-host or pin fixed files with SRISSDF PW.4; PCI DSS 6.4.3 integrityA changed file is refused by the browser./domains/appsec/browser-security/security-headers/
Strict CSP script-src without wildcardsDefense in depthLimits which origins can run code; blocks follow-on loads from new domains./domains/appsec/browser-security/cors-csp-browser-boundaries/
Watch ownership and reputation of script originsSSDF PW.4; supplier reviewA domain sale is a supplier change; review it as one./frameworks/supplier-software-assurance/
Change and tamper detection on payment pagesPCI DSS 11.6.1Detects new or modified scripts and headers as browsers receive them./frameworks/pci-dss-requirement-6/

Lessons that still apply

  • Every external script tag delegates control of your page to whoever owns that domain, now and after any future sale.
  • Treat a change of ownership of a dependency, a domain or a maintainer account as a security event, as in /domains/supply-chain/dependencies/case-study-event-stream/.
  • Prefer fixed, versioned files you can hash over services that generate code per request.
  • Warnings arrived four months early; an inventory with owners is what turns a warning into a removal.
  • Selective payloads evade owners' own testing: check pages from the outside, as different devices see them.

What the public evidence does not establish

The public record does not establish how many sites served malicious code to visitors, as opposed to embedding the script, or how many visitors were redirected; estimates of affected sites ranged widely and changed as researchers rescanned. Whether the injected code did anything beyond redirects, such as collecting form data, is not documented in the primary reports. The US Treasury's description of Funnull altering a code repository in 2024 matches polyfill.io but does not name it.

Guided practice

It is 2024-06-26. You are responsible for four web properties. Decide whether each is exposed to the polyfill.io takeover and what to change. Treat the facts as true.

Supplied scenario material
Plain text
W1: Marketing site; template includes <script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=default">; no CSP; served directly, not through Cloudflare.
W2: Checkout page; loads polyfill.io through a tag manager container; CSP script-src includes https://*.googletagmanager.com and https://cdn.polyfill.io; PCI DSS in scope.
W3: Internal docs generated with pdoc 14.4.0 --math, hosted on an intranet.
W4: Web app; self-hosts core-js 3.37.1 from /vendor with an integrity attribute; CSP script-src 'self'.
  1. 1. Assess W1

    Is W1 exposed, and what is the fix?

    Expected result and reasoning

    Yes: every visitor's browser runs whatever cdn.polyfill.io returns. Remove the tag, or, if older browsers still need polyfills, self-host a fixed build with SRI. Add a CSP so a future third-party script cannot be loaded without review.

  2. 2. Assess W2

    Why is W2 the most serious, and what is required?

    Expected result and reasoning

    It is a payment page, so injected code could read card data, and the CSP explicitly allows cdn.polyfill.io. Remove it from the tag manager and the CSP at once, review the page's script inventory and integrity controls under PCI DSS 6.4.3, and check 11.6.1 change-detection alerts and logs for unexpected scripts since February.

  3. 3. Assess W3

    What about the pdoc output?

    Expected result and reasoning

    pdoc before 14.5.1 with --math linked polyfill.io (CVE-2024-38526), so readers' browsers loaded it. Upgrade pdoc to 14.5.1 or later and regenerate the documentation; being on an intranet does not help, because the browser fetches the script from the internet.

  4. 4. Assess W4

    Is W4 exposed?

    Expected result and reasoning

    No: the polyfill code is a fixed, self-hosted file with an integrity hash, and the CSP allows only same-origin scripts. Keep core-js updated through normal dependency management.

  5. 5. Prevent the next one

    Name the process change that would have acted on the February warning.

    Expected result and reasoning

    Keep an inventory of every third-party script origin with an owner, subscribe owners to advisories and ownership changes for those origins, and require review before a new origin is added to a page or a CSP.

Your deliverable: A table for W1 to W4 (exposed or not, why, action) and a one-paragraph third-party script policy.

The sites are invented. Real exposure depends on whether a page actually loaded the script during the malicious period, which only browser-side monitoring or server logs of script references can show; the payload was selective, so absence of a redirect in your own test proves little.

Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.

No draft saved on this device yet.

Sources & further reading

The standards, specifications and documentation behind the guidance above. Each is the evidence for a claim on this page; consult it for detail beyond the defaults and versions listed here.

  1. Sansec: Polyfill supply chain attack hits 100K+ sites (2024-06-25, with updates)
  2. Cloudflare: Polyfill.io now available on cdnjs, reduce your supply chain risk (2024-02-29)
  3. Cloudflare: Automatically replacing polyfill.io links with Cloudflare's mirror (2024-06-26)
  4. polyfill-service README before the sale: the service reads the User-Agent header of each request. Commit 0b850d5 (20 October 2023).
  5. GitHub Advisory GHSA-5vgj-ggm4-fg62: pdoc embeds link to malicious CDN (CVE-2024-38526). Published 25 June 2024.
  6. US Treasury: Treasury takes action against major cyber scam facilitator (2025-05-29)
  7. MDN: Subresource Integrity
  8. MDN: Content-Security-Policy script-src

Reference information

Download this guide as Markdown