Short Term: The First Ninety Days
The first ninety days cover four kinds of change, and an engineering team can make all of them without rearchitecting anything or reopening a contract. Find out what loads. Remove what nobody needs. Pin, constrain and read what stays. Put a manual check in front of the decisions that move money or equipment.
Set one expectation before starting, because it changes what your team looks for. The code doing the damage is usually valid, well-formed, correctly signed code from a source everyone agreed to trust, doing something it was never meant to do. Nobody here is hunting for a bad file. You are reducing how many outside parties can put a file on your interfaces at all, and making each one that stays accountable to somebody.
Third-Party Script Inventory
Start where the consequences concentrate: login pages, payment pages, transaction approval screens, operator dashboards. For each one, list every script, style, font, frame, pixel and tag the browser loads, and the address each one comes from. Browser developer tools produce that list for a single page. Crawler-based scanners produce it across a property. A Content Security Policy in report-only mode produces it continuously, from real user sessions, which makes CSP a discovery tool before it is ever an enforcement one.
Expect the count to exceed what your application declares, and treat the gap as the finding. Loaded code fetches further code. A tag manager injects whatever its containers hold at the moment of the request. Neither shows up in a build-time manifest. Report-only CSP is the cheapest way to measure that difference, because it reports what the browser attempted rather than what the repository claimed.
Give tag managers their own pass, because a tag manager lets other people add code without asking you first. List the containers, list who can publish through them, and list what each container injects. In most mature deployments the publish list is longer than anyone expects, and the container contents have outlived the people who added them.
Run the same exercise on the build pipeline using a software bill of materials. SBOM tooling enumerates the open-source dependency tree that becomes your shipped bundle. The file it produces is not the goal. Knowing whose code runs on your Glass is the goal, because most of what runs there was written by people nobody on your payroll has met.
Rank what you find by what each resource can reach, not by how large its vendor is. For each one, record what it can touch once it loads, who operates the address it comes from, and what breaks if you remove it. A small vendor with code inside a payment flow outranks a large one loading a font on a marketing page. That ranking drives every removal, pinning and self-hosting decision you make after this.
You now have a number for each high-stakes interface: how many outside parties reach it. From here you make that number smaller and hold the remainder accountable.
Removing Unnecessary Third-Party Code
Every third-party resource is code somebody else controls, running on your interface, changed at their discretion. Fewer of them is the cheapest remediation available to any organization.
Delete unused and orphaned scripts first. Most mature web properties carry tags whose owners have left the company and whose purpose nobody can state. Removing a resource nobody will claim needs no analysis.
Ask one question of everything that survives, and it is not a technical question. Most of these were added by reputation: a library or a service gets adopted because it is popular, well branded and widely used, and nobody verifies its behavior or its intent afterward. Widespread adoption feels like herd immunity and is nothing of the sort. So for each remaining resource, ask what anyone actually established about it before it was let in, and treat "everyone uses it" as a missing answer rather than an answer.
Hold high-stakes pages to a stricter standard than the property at large. A payment page does not need a chat widget, a social pixel or a heat-mapping script. Interfaces where a substitution would do the most damage carry the smallest dependency set the business function tolerates, and every resource left on them has a named owner and a stated purpose.
Freeze additions while the cleanup runs. Require sign-off for any new third-party resource until the inventory settles, so the list does not grow behind the team reducing it.
A component you never took on cannot be sold to someone else, cannot be compromised, and cannot quietly start returning something different. Removal is the only change in these ninety days with no structural limit, because everything else here manages risk that is still there and this one deletes it.
Pinning, Self-Hosting, and the CDN Question
Whatever survives the cull gets pinned, brought in-house, or both.
SRI confirms that the fetched resource matches a hash you already trust, which closes the gap between what you reviewed and what was served, for that resource, on that load. It does real work wherever content is static and versioned. Two limits are structural. SRI breaks where content updates continuously or is generated per request, because no advance hash exists to check against. And its declarative form covers script and link elements only. A resource fetched by already-executing code can carry integrity metadata, because the Fetch standard gives every request an integrity field, but that check is opt-in per call, required by nothing and audited by nothing. Apply SRI everywhere content can be pinned, require the integrity field on runtime fetches your own code makes, and write down every place you could not apply it as a flagged gap rather than a silent one.
Self-hosting goes further, and it removes an external supplier from your load path faster than anything else available today. Pull the library once, review the copy, and serve it from infrastructure you operate. Your users stop calling out to anyone else for that code, often within days of the decision. The cost is real and worth stating plainly: you take over configuration and update management by hand. New versions arrive through a review you schedule instead of whenever the remote endpoint ships them. That cost is the point. Approving a source is not the same as approving whatever that source later returns, and once you serve the copy yourself there is no later return to approve.
Self-hosting removes the supplier from your delivery path, and that is the whole of what it removes. It says nothing about where the copy you took came from, so review that copy before you serve it. And a self-hosted script that still fetches data from third-party endpoints at run time leaves that route open, because the change rides the data rather than the file. A hash-valid, verifiably clean script does the damage using whatever its endpoint sends it at execution. Self-hosting a script does not self-host its endpoints.
Your CDN contract is also something you assigned, which means you can reassign it. Migration between major CDN providers is a well-worn operational path measured in days to weeks, and it is the right move when diligence turns up an operator you would not choose today. Frame the move accurately: switching providers substitutes one company for another rather than removing anyone from the path, so the ownership question travels with the migration. Ask who operates the infrastructure you are moving to. And migrating covers only the CDNs you contract with. A third-party script chooses its own delivery path, and the remedy for that path is self-hosting or proxying, not your CDN agreement.
Pin your versions. Exact dependency versions, committed lockfiles, no floating ranges. An auto-updating dependency approves a package name once and then accepts whatever that name later resolves to. Impose a cooldown on upgrades, adopting new versions days or weeks after release rather than immediately, so a compromised release surfaces in the ecosystem before it reaches you. Disable package install scripts where your toolchain allows it.
Pinning and self-hosting are worth deploying and they move real risk quickly. They verify and control what gets loaded. They do not observe what loaded code then does.
Content Security Policy and the PCI DSS Pattern
Content Security Policy limits which addresses a browser may load from. Its value is real. It removes whole classes of injection outright, and its violation-reporting channel is the cheapest continuous telemetry a defender currently has on what a page tries to load. Deploy it report-only first, then enforce. Its limit is equally real. CSP governs which addresses are approved, not what an approved address sends back, and not what the returned code does once it runs. A URL does not return one fixed resource to everyone, and checking one fetch does not protect any later fetch. An allowlist of addresses does not touch that, because the response is built per request by whoever controls the endpoint at that moment.
Content Security Policy Level 3 lets a hash source expression match an external script when that script element also carries matching integrity metadata. That is genuine content pinning inside the policy rather than address approval. It reaches script and style elements only, it requires the integrity metadata alongside it, and it is checked at load like everything else. Use it on high-stakes interfaces where content is stable enough to pin, and do not let it stand in for coverage it does not have.
PCI DSS 4.0, in requirements 6.4.3 and 11.6.1, obliges script inventory, authorization and change detection on payment pages, and a commercial tooling category has grown up to meet it. Payment pages are where the rule applies. Any interface where somebody moves money or operates equipment deserves the same three verbs: inventory, authorize, detect change.
Constraining where content comes from is worth deploying, and it stops at what that content does.
Static Review and Sandboxed Execution
The inventory tells you what loads. Static review and sandboxed execution tell you what the survivors do. Both are available now, and both hand you findings a person still has to judge rather than verdicts. Every pattern below has legitimate uses, so its presence means look closer, not convict.
Search retained scripts, first-party and third-party alike, for eval, new Function, string arguments to setTimeout and setInterval, and document.write. These are the mechanics of code generating more code while it runs, so that what ultimately executes never existed in a form anyone could inspect beforehand. Existing linters and static-analysis rules surface them in minutes.
Give decoding of fetched data the most respect. atob and equivalent base64 or hex decoding applied to content fetched at run time is how fetched data becomes executable, or becomes displayed truth, at the moment it is read. It is also how a change arrives in the data rather than in the file. A clean file that decodes whatever its endpoint sends has moved the weapon into data that no pre-delivery inspection will ever look at.
Remove 'unsafe-eval' from your CSP. The browser then refuses string-to-code execution outright, which converts one of those indicators into an enforced control. Test in report-only mode first. Some legitimate libraries still depend on it, and each one that does is worth knowing about.
Finish with dynamic analysis. Execute third-party scripts in an instrumented environment, either a headless-browser harness with a mutation observer or the commercial client-side protection tools you may already have deployed for the PCI requirement, and record what each script touches: form fields, displayed values, links, injected and removed nodes. Baseline that behavior and alert on drift. State the limit without apology: a sandbox is served whatever variant the endpoint chooses to send it. The endpoint decides what your scanner receives too, and conditional delivery is the practice built on exactly that, so clean behavior in your lab is evidence about production rather than proof of it. Baseline-and-drift is worth having anyway. It catches the broad case and raises the cost of the narrow one.
Reading the survivors is the closest these ninety days get to watching behavior, and it examines copies served to an observer rather than the render in front of your customer. Deploy it for what it is.
Monitoring Suppliers for Change of Control
Suppliers you cannot remove can at least be watched for a change of owner. Polyfill.io became an attack through exactly that: the cdn.polyfill.io domain changed owners in 2024, and the existing script tags pointing at it kept fetching from the same address afterward. The infrastructure changed hands. The trust did not. You are watching so that you see the next one in time.
Monitor the domains and endpoints your inventory names. Registration changes, expirations and certificate transparency events, for every third-party origin that reaches a high-stakes interface. Subscribe to supply-chain and vulnerability advisories covering the packages your SBOM names. Both are commodity capabilities today.
A change of owner leaves no technical signature. A company sale, a stolen credential, a maintainer or namespace transfer, a DNS repoint: none of those alter the address, the hash or the certificate, so everything a scanner measures stays green while the party deciding what that asset serves becomes somebody else. And the interval that follows is what defeats detection. An acquired asset is typically operated normally for months or years, serving exactly what its dependents expect, because a normally operating service produces nothing to find. Often the first detectable event is the attack itself. Together they say what this watch is for. It watches ownership and control rather than content, and its whole value is that it can fire during the interval when nothing else can.
Then look at your own property the way an investigator would. Schedule fetches of your high-stakes pages from different networks, geographies and device profiles, and compare the results against each other. A difference between vantage points is one of the few observable signs of conditional delivery you can currently produce, and it works best against the widest cases: a swap served to everyone, or to a whole country, shows up as soon as one vantage point sits inside the target set and another sits outside it. The limit is honest and it stands. A swap conditioned on anything your vantage points cannot express, such as who is signed in, what they did before, which group they were put in, or which experiment bucket they landed in, reaches that group and no observer you operate, down to one named person on one device. Run the comparison anyway. It raises the cost of broad substitution, and the mid-term horizon grows it into an automated program.
Watching for a change of owner turns something that leaves no technical trace from a historical explanation into an operational warning. It shows you that control moved. It does not show you what the new owner does with it.
Out-of-Band Verification Procedures
Attack the Glass deceives a person into acting correctly on wrong information. A trained, authorized operator does exactly the right thing in response to a false reading, and no system anywhere records that anything went wrong. Until rendered content can be verified technically, your countermeasure is a second, independent path to the truth, and that countermeasure is procedural, cheap and available now.
Give irreversible actions out-of-band verification. Confirm wire transfers, payment-detail changes, credential resets and destructive operational commands through a separately established channel before execution, never through the screen that requested them. In 2024 a finance employee at the Hong Kong office of the engineering firm Arup transferred roughly twenty-five million dollars after a video call in which every other participant was a synthetic likeness of a colleague. The screen was accepted as sufficient authority. The procedure exists so that no single rendered surface is ever sufficient authority again.
Give critical operational readings a second source. Where a display drives a consequential decision, whether that is a grid state, an account balance or a system alert, name the independent source that confirms it and the moments when that check is mandatory. The check does not have to be continuous to work. It has to exist at the moments that matter.
And give every operator, administrator and finance officer one sentence of standing doctrine: a screen that contradicts a system of record is a security event. Give that report a named destination that takes it seriously. The person staring at the discrepancy is, for now, the only sensor you have deployed at the rendering surface, and the reporting path is what makes that person part of your defense.
Procedure is your one defense at the same layer as the attack, the layer where a human decides. It is also the one that degrades fastest without rehearsal, which is why the mid-term horizon puts it on a tabletop.
Briefing Leadership, Finance, and Operations
The people most worth deceiving are the ones who approve payments, administer systems and operate infrastructure, and in most security programs they are briefed last or not at all. Correct that in the first ninety days. Leadership, finance and operations hear the shape of the threat in plain terms: what a substitution looks like, why a trusted source and a clean-looking page prove nothing, and which verification procedures now apply to them personally.
An attacker chooses who receives the change, and the range runs from every user of an application, through a country or a language community or a group defined by behavior, down to one named person on one device, with different people served different content at the same moment. And the code that delivers it is usually not malware, so your tools stay quiet.
Put the synthetic-likeness video call, the altered payment page and the false dashboard reading alongside phishing in your standing curriculum, under one rule that covers all of them: verify through a second channel before acting on a screen alone.
What Carries Forward
Ninety days of disciplined execution leaves you a shorter list of outside suppliers, each one better held, watched for a change of owner, with a second path to the truth wherever a screen can move money or equipment. Each high-stakes interface now has a count, and somebody owns it.
None of that changed how your interfaces get built. You governed the suppliers. You did not change how a browser or an app puts your interface together on somebody's device. Sandbox analysis and vantage-point comparison both examined what was served to an observer, which is not the same as what renders on a targeted device. That is what the mid-term horizon takes up.