Web Component Security Model

The sandbox boundary for third-party web components — what a component can and cannot access, and how bundles stay immutable

web-componentssecuritysandbox

Third-party code never runs with platform trust. Every web component executes inside a layered sandbox, and the only way data gets in is a validated message bridge carrying data the user is already permitted to see. Outbound, the sandbox blocks all network APIs — with one honest limit on navigation, described below.

The sandbox boundary

Components run in an <iframe sandbox="allow-scripts"> without allow-same-origin, which gives the document an opaque origin. On top of that, the sandbox page ships a strict Content Security Policy with no connect-src.

A component can A component cannot
Render freely inside its shadow DOM Read platform cookies, storage, or the parent page's DOM
Receive granted data over the bridge (init/data messages) Make network requests — fetch, XHR, and WebSockets are blocked by CSP
Receive the platform theme and follow light/dark live Load scripts or assets from external URLs
Report its height (resize) and errors over the bridge Obtain tokens, sessions, or an API client — none exist in the sandbox

Every bridge message carries a per-instance random nonce and is validated on both sides; messages from unrelated frames are ignored.

Immutable, pinned bundles

  • Bundles are uploaded to platform storage — no arbitrary external URLs, so the main application's CSP stays tight.
  • Each upload creates a new immutable version; nothing is ever overwritten.
  • The bundle's SHA-256 is pinned at registration and re-verified on every serve — if stored bytes ever differed from the pin, the platform refuses to serve a single byte.
  • Uploads are capped at 10 MB and must be a single ESM file defining the registered custom element.

Why there are no tokens in v1

Earlier integration approaches injected third-party scripts into the main page with access to a token service and API client — full trust. This system replaces that with a real boundary: the platform resolves permissions server-side from the registration's declared scopes and pushes only the resulting data. Since the component never authenticates, there is nothing to steal, replay, or escalate — compromising a component's code yields at most the data its consented scopes already forward.

The honest limit

Data that is granted is, by definition, handed to third-party code — and once handed over, its confidentiality cannot be technically guaranteed. In particular, CSP blocks every network API but cannot stop a component from navigating its own frame to an external URL that embeds data it already received over the bridge. The sandbox strictly limits what a component can ever obtain (only the data its consented scopes forward — never tokens, sessions, or other users' data), not what a malicious component might do with that data afterwards.

That is exactly why the consent overlay discloses every declared scope before the first run, and why a disabled registration acts as an immediate kill switch — placed widgets render a neutral placeholder instead of loading any code.