Web Component Security Model
The sandbox boundary for third-party web components — what a component can and cannot access, and how bundles stay immutable
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.
Related
- Permissions — the declared-scope vocabulary
- Consent behavior — end-user disclosure
- Building Web Components — the authoring guide