{"record":{"id":"09a9dd20cc288fee","repo":"oxc-project/oxc","slug":"using-polyfill-io-is-a-security-risk-due-to-a-supp","errorCode":null,"errorMessage":"Using polyfill.io is a security risk due to a supply chain attack in 2024.","messagePattern":"Using polyfill\\.io is a security risk due to a supply chain attack in 2024\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"critical","filePath":"crates/oxc_linter/src/rules/nextjs/no_unwanted_polyfillio.rs","lineNumber":26,"sourceCode":"use oxc_semantic::AstNode;\nuse oxc_span::Span;\n\nuse crate::{\n    context::{ContextHost, LintContext},\n    rule::Rule,\n    utils::{NEXT_POLYFILLED_FEATURES, find_url_query_value, get_next_script_import_local_name},\n};\n\nfn no_unwanted_polyfillio_diagnostic(polyfill_name: &str, span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(format!(\n        \"No duplicate polyfills from Polyfill.io are allowed. {polyfill_name} already shipped with Next.js.\"\n    ))\n    .with_help(\"See https://nextjs.org/docs/messages/no-unwanted-polyfillio\")\n    .with_label(span)\n}\n\nfn polyfill_io_security_warning(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\n        \"Using polyfill.io is a security risk due to a supply chain attack in 2024.\"\n    )\n    .with_help(\"Replace with a safe alternative like https://cdnjs.cloudflare.com/polyfill/ or use modern browser features directly. See: https://blog.cloudflare.com/polyfill-io-now-available-on-cdnjs-reduce-your-supply-chain-risk\")\n    .with_label(span)\n}\n\n#[derive(Debug, Default, Clone)]\npub struct NoUnwantedPolyfillio;\n\ndeclare_oxc_lint!(\n    /// ### What it does\n    ///\n    /// Prevent use of unsafe polyfill.io domains and duplicate polyfills.\n    ///\n    /// ### Why is this bad?\n    ///\n    /// **Security Risk:**\n    /// The domains `cdn.polyfill.io` and `polyfill.io` were compromised in a supply chain attack in 2024,","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/nextjs/no_unwanted_polyfillio.rs#L8-L44","documentation":"Diagnostic from the oxlint rule nextjs/no-unwanted-polyfillio (correctness category). It fires when a JSX <script> element (or a next/script component) has a string-literal src pointing at the compromised polyfill.io service: the domain was acquired by a malicious actor in a June 2024 supply-chain attack that injected hostile code into 380,000+ websites. Because the domains cdn.polyfill.io and polyfill.io are permanently tainted, any reference is reported as a security defect, not a style nit. The rule only runs on JSX sources and only matches literal src strings, so a dynamic src={'https://polyfill.io/...'} expression is not caught.","triggerScenarios":"A JSXOpeningElement whose tag name is 'script' or matches the local import name of next/script, carrying a src attribute of type JSXAttributeValue::StringLiteral whose value starts with 'https://cdn.polyfill.io/v2/' or 'https://polyfill.io/v3/'. The security warning is emitted before any other check in run() and returns early, so it takes precedence over the duplicate-polyfill diagnostic that covers cdjs.cloudflare.com/polyfill and polyfill-fastly.* URLs.","commonSituations":"Legacy HTML boilerplate or pre-2024 tutorials pasted into Next.js layouts; marketing pages kept a <script src='https://cdn.polyfill.io/v2/polyfill.min.js'> tag for old-IE support; teams auditing after the 2024 incident news; enabling the nextjs plugin in .oxlintrc.json for the first time on an old codebase.","solutions":["Delete the polyfill.io <script> tag entirely and rely on the browsers/transpilation targets your project already supports","If a polyfill is genuinely needed, switch to a safe mirror such as https://cdnjs.cloudflare.com/polyfill/ and request only features Next.js does not already ship (the rule's duplicate-polyfill check will then keep you honest)","Prefer bundling polyfills through core-js/browserslist or importing them in _app instead of any third-party CDN","Add nextjs/no-unwanted-polyfillio to CI with --deny-warning so the compromised domain cannot be reintroduced by template copy-paste"],"exampleFix":"// before (pages/_document.jsx or a component)\n<script src='https://cdn.polyfill.io/v2/polyfill.min.js'></script>\n\n// after: remove it, or use a safe mirror with only needed features\n<script src='https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=IntersectionObserver'></script>","handlingStrategy":"validation","validationCode":"# .oxlintrc.json\n\"rules\": { \"nextjs/no-unwanted-polyfillio\": \"error\" }\n\n# CI gate\nnpx oxlint -c .oxlintrc.json --deny-warning .\n\n# belt-and-braces grep over templates (catches non-JSX HTML too)\ngrep -RInE 'https?://([a-z0-9.-]+\\\\.)?polyfill\\\\.io' app components public || echo clean","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat third-party script tags in JSX like dependencies: review them in PRs and pin to vetted CDN mirrors only","Prefer bundler-managed polyfills (core-js/browserslist) over runtime CDN injection so URLs never enter source","Subscribe to supply-chain advisories (GitHub Dependabot, npm advisories) so a compromised domain surfaces before lint does"],"tags":["nextjs","security","supply-chain","polyfill","jsx","oxlint"],"backgroundTag":"compromised-cdn-script","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}