Third-party scripts are a supply-chain risk
A third-party script is code you did not write, served by a company you do not control, running inside your pages with your users’ sessions — in supply-chain terms, it is a dependency that can change its own code at any moment without a release, a review or a notification. In October 2026 that abstract risk became a concrete loss, as reported, when a performance-monitoring script loaded by a major trading site was swapped for one built to drain its users. This article goes through what was reported, what Subresource Integrity actually stops, why pinning a version comes before hashing it, when self-hosting is the better answer, and the short audit routine that keeps the inventory honest.
What happened at Polymarket, as reported
On 29 October 2026, according to the reporting, Polymarket’s international site put trading into cancel-only mode after users reported funds leaving their accounts, and trading resumed roughly an hour later. The company’s own statements, as reported, attributed the incident to a compromised third-party provider, said the matter had been referred to law enforcement, and promised to make affected users whole. Reports based on the company’s 30 October postmortem put the amount taken in the attack at about $2.7 million, with total refunds of $3.9 million once goodwill payments for losses caused by the trading pause were included; independent on-chain analyses reported in the coverage traced a similar figure, around $2.7 million, across several hundred wallets. None of those figures is independently verified here — they are the reporting’s figures, and this article treats them that way.
The mechanism, as reported, is the part that generalises. Researchers cited in the coverage linked the malicious code to the real-user-monitoring (RUM) script of a website performance vendor — a small file whose entire job is to measure page-load speed in visitors’ browsers, embedded per customer from the vendor’s CDN. That file, the reporting says, was replaced with a payload written specifically for the target site: it worked inside logged-in sessions, used the site’s own trading machinery already present in the page, and moved positions and funds to an attacker-controlled destination. Per the reporting, no smart-contract flaw and no stolen passwords or private keys were involved — the swapped script simply was the page, as far as the browser was concerned. Whether every detail of that account survives later scrutiny is, for the lesson, beside the point: the attack shape — one vendor file, swapped once, executed by every visitor — is the standard shape of this class of incident.
A third-party script is your code, with someone else’s release process
There is no sandbox in a classic script tag. Code loaded from a vendor’s CDN executes in your origin with the same access as your own bundle: it can read and change the DOM, read form values as they are typed, call your APIs with the user’s session, and send what it finds anywhere. The OWASP guidance on third-party JavaScript names the three risks plainly — loss of control over changes to the client application, execution of arbitrary code on client systems, and disclosure of sensitive information to third parties — and all three follow from that one fact. Your review process, your code freeze and your change board end at your own build; the vendor’s file can be edited by the vendor, by an attacker who compromises the vendor, or by whoever re-registers the vendor’s domain if it ever lapses.
That asymmetry is why “we trust this vendor” is not a control. Trust decides whose script you accept today; it says nothing about what that URL serves tomorrow, and it gives you no signal when the content changes. The controls that work are the ones that either remove the trust (serve the file yourself), constrain it (a Content-Security-Policy that limits what any script may do and where it may send data), or verify it mechanically on every load — which is what Subresource Integrity is for. Start with the inventory: you cannot apply any of the three to scripts you do not know you have, and most sites have more than their owners expect once marketing tags, support widgets and monitoring embeds are counted.
What SRI does — and what it does not stop
Subresource Integrity is a browser mechanism, specified by the W3C and documented by MDN: the page states the cryptographic hash of the file it expects in the tag’s integrity attribute, the browser hashes what it actually downloaded, and on any mismatch it refuses to execute the file. For a cross-origin file the tag also needs crossorigin="anonymous" and the server must send CORS headers, so the browser is allowed to make the check. Applied to a fixed library, SRI converts a silent swap into a loud failure: the file that arrives is either byte-for-byte the file you reviewed, or it does not run at all. Had the monitoring script in the reported incident been a fixed file loaded that way, a swapped payload would have failed the check instead of running in users’ sessions — that conditional is the honest form of the claim, because whether that particular embed was hashable is a question about a dynamic per-customer file, which is exactly the hard case below.
What SRI does not stop is just as important. It does not help for scripts whose content is supposed to change — tag-manager containers, personalisation and consent loaders, per-customer generated files — because there is no stable hash to pin. It does not judge the file you hashed: a faithfully delivered script can still be nosy, sloppy or malicious by design, and SRI will happily verify it forever. It does not cover scripts injected at runtime by other scripts, it does not protect first-party files served from your own infrastructure, and it does nothing about the data a legitimate script collects once it runs. SRI is one mechanical guarantee — these exact bytes, or nothing — and it earns its place alongside, not instead of, CSP, vendor review and minimising how many third parties get session-level access in the first place. This site’s security headers check grades the CSP side of that pairing.
Pin the version, or the hash cannot hold
SRI has a precondition that fails more audits than malice does: the URL must always return the same bytes. A hash pinned over a latest address, a floating major version, or a per-customer file the vendor regenerates is a hash that breaks the first time the vendor ships an update — and teams that have been paged once by a self-inflicted SRI failure tend to remove the attribute rather than fix the address, leaving the page worse than unmeasured. The durable order is the reverse: first change the URL to name the exact version ([email protected], /v2.4.0/), review that version once, then hash exactly that file. Version bumps then become deliberate events — new URL, new hash, one commit — instead of ambient changes you discover from your users.
Pinning has a cost, and honesty requires naming it: a pinned library no longer receives silent security patches from the vendor, so pinning creates a duty to track releases and bump on purpose, the same duty any vendored dependency carries. That is a manageable duty — release notes and a calendar reminder cover it — and it is strictly better than the alternative it replaces, in which updates arrive unreviewed at the vendor’s pace and breakages arrive unannounced at yours. Treat the pair as one practice: the pin makes the content addressable, the hash makes it verifiable, and the audit makes sure neither has quietly rotted — a version string edited out by a CMS migration is as common as a missing hash, and only slightly harder to notice by eye.
Self-hosting, and the audit routine
For the scripts that matter most, there is a blunter option: serve the file yourself. Self-hosting removes the vendor’s server, account and domain from your runtime supply chain entirely, lets your normal deployment pipeline review and hash the file, and keeps working when the vendor’s CDN does not. The trade-offs are real — you give up the vendor’s edge cache and automatic updates, you take on the duty to ship their security fixes yourself, and some vendors’ licences or per-customer generated files simply do not allow it — so the sensible split is by criticality: self-host small, fixed libraries on sensitive pages; pin and hash what stays on a CDN; govern (with publisher restrictions, change review and CSP) the dynamic tags that can be neither pinned nor hosted; and delete, on sight, any script nobody can justify. Every removed script is a supply chain you no longer have.
The routine that keeps this true is short enough to actually run. Quarterly — and after any redesign, CMS migration or new vendor — run the third-party script and SRI audit over your key page templates: it lists every script the static HTML references, which are third-party, which lack integrity, and which are unpinned, and computes a paste-ready sha384 for the first missing hashes it can fetch. Fix in impact order — session, payment and personal-data pages first — then check the transport and header layers with the SSL grader and the headers check, and finish in the tag manager, whose runtime-injected scripts a static audit cannot see: review its publisher list and change history with the same suspicion you would give a deploy log. Fifteen minutes a quarter, against an incident class whose reported cost, in the case above, ran to millions in about an hour.