Fix guide · medium · missing_sri_external_script
External script loaded without Subresource Integrity
A <script src="https://..."> tag pulls JavaScript from a third-party host, but has no integrity= attribute. The browser will execute whatever bytes the CDN returns — even if those bytes were swapped by an attacker.
Why it matters
Subresource Integrity (SRI) is the browser-enforced check that the bytes a CDN serves match a hash you ship in your HTML. Without it, every cross-origin <script> tag is a trust delegation: you are saying "I trust whoever runs this CDN, plus whoever can compromise their build pipeline, plus whoever can intercept the connection from the CDN to my user, with full code-execution rights on every page my users load."
That trust has been violated repeatedly:
- polyfill.io (2024) — A Chinese company bought the domain and started shipping malware to ~100,000 sites overnight. Sites with SRI hashes were unaffected (the hashes didn't match, so browsers refused to execute). Sites without SRI silently served the malicious code.
- Browsealoud / Coinhive (2018) — UK government sites and ~4,200 others started mining crypto for an attacker who compromised the Browsealoud accessibility CDN.
- event-stream (2018) — Not a CDN compromise but illustrative: a popular npm package was handed off to a malicious maintainer who shipped a Bitcoin-stealing payload. Anyone who pinned an unsigned, unhashed version got the payload.
The fix is cheap (one extra HTML attribute), the failure mode is catastrophic (full XSS-equivalent code execution on every page), and the attacker only has to compromise one CDN to hit thousands of sites at once. SRI is the single highest-leverage hardening step you can take against supply-chain attacks.
How to fix it
Add an integrity= attribute (and crossorigin="anonymous") to every cross-origin <script> and <link rel="stylesheet"> tag.
1. Generate the hash for the asset you're including:
curl -sL https://cdn.example.com/lib.js | openssl dgst -sha384 -binary | openssl base64 -A
2. Add it to the tag:
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
3. Pin a specific version. SRI is meaningless if you point at @latest — the hash will mismatch on every release. Use a version that won't move, e.g. /[email protected]/ not /lib/.
Tooling:
- SRI Hash Generator — paste a URL, get the tag.
- Most build tools (Vite, Webpack, Next.js) can emit SRI hashes for your own bundles automatically; check your config docs for "subresource integrity" or "sri".
- For self-hosted CDN-style assets, regenerate the hash at build time and inject it into the HTML template.
Better alternative when feasible: self-host the dependency. npm install it, bundle it with your own JavaScript, and serve it from your origin. SRI becomes unnecessary because there's no third-party trust to extend.
What NOT to do: removing the crossorigin attribute. Without crossorigin="anonymous", the browser can't read the response body to check the integrity hash, and it falls back silently to executing the script. Both attributes are required together.
Did vibecheck flag this on your app?
If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.
Run another inspection