# Sandboxed iframe same-origin script escape

iframe sandbox with allow-scripts plus allow-same-origin on attacker-influenced content

_Area: Web injection and code execution. Seen in 2 disclosed reports; latest 2026-08-04. Updated 2026-11-07._

## How it works
An iframe `sandbox` attribute is meant to isolate untrusted content. Two tokens defeat it when combined: `allow-scripts` lets the framed document run JavaScript, and `allow-same-origin` gives that document the embedding page's origin. Together the framed content is not sandboxed at all — its scripts run with the parent's origin, so they can read the parent's DOM, `localStorage` and cookies, and make credentialed calls to the app's own API. The broken assumption is that "it is in an iframe, so it is contained." Containment only holds if the framed document is treated as a different origin; granting both tokens silently removes that boundary [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md)[E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-5vpr-4fgw-f69h.md). The same reach appears without an iframe when a client-side interpreter runs in a same-origin worker: Python's `js` bridge and `pyfetch` inherit the page origin and send the victim's cookies, making stored user code an XSS primitive [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md).

## Where it shows up
- A static `sandbox` string containing both `allow-scripts` and `allow-same-origin`, especially hardcoded rather than computed per content [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md).
- A second rendering path (e.g. a `serveUrl` branch) that did not inherit hardening applied to an older path (`srcdoc` with same-origin opt-in plus injected CSP) [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md).
- Document viewers that enable scripted content: `allowScriptedContent: true` passed to a library like epub.js [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-5vpr-4fgw-f69h.md).
- Proxies that serve user files from the application's own origin and add no CSP by default, leaving the sandbox as the only boundary [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md).
- Same-origin web workers running Pyodide/WASM interpreters over user-supplied code [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md).

## How to review
Find every iframe and worker that renders attacker-influenced content. The invariant: untrusted content must never hold both script execution and the parent's origin. Check that `allow-same-origin` is absent, or that content is served from a distinct origin (separate host/port) or under a CSP that blocks inline script. For each rendering path, confirm the sandbox value is not a copied static string but derived from whether the content is trusted. For interpreters, ask whether user code can be stored and later executed by another user in a same-origin context [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md).

## How finders spotted it
- Diffed two preview paths in one component: the `srcdoc` branch had been hardened, the newer `serveUrl` branch kept a static sandbox with `allow-same-origin`; also noted the terminal proxy mounts on the app's own origin and adds no CSP unless configured [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md).
- Noticed `allowScriptedContent: true` passed to a viewer library, recalled the library's docs warn against it, and reasoned the combined sandbox tokens are ineffective; matched the pattern to a prior CVE in another product [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-5vpr-4fgw-f69h.md).
- Recognized that a same-origin Pyodide worker's `js` bridge and `pyfetch` carry the victim's cookies, so no separate XSS sink was needed [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md).

## Variants seen
- Hardcoded sandbox on a file-preview iframe serving same-origin files [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md).
- Viewer library option enabling scripted EPUB content inside a dual-token sandbox [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-5vpr-4fgw-f69h.md).
- Same-origin web worker interpreter acting as an XSS primitive via stored chat code [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md).

## Impact and severity
High: stolen `localStorage` token or JWT yields account takeover [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md)[E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-5vpr-4fgw-f69h.md). Impact rises when the victim is an admin or holds `workspace.functions`, extending takeover to server-side code execution through Functions [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md); a low-privilege uploader stealing an admin JWT escalates to full admin session [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-5vpr-4fgw-f69h.md).

## Seen in real software
Open-source packages and projects, and products, where this pattern was found. If the target runs one, compare the version it runs with the fix.
- **open-webui (pip)** >= 0.9.0, <= 0.10.2, fixed in 0.11.0: Open WebUI: Same-origin XSS to account takeover via terminal file-preview iframe hardcoding allow-same-origin (CVE-2026-70486). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md. Fix: https://github.com/open-webui/open-webui/commit/65a5fad7b97db99d490d81f4e0860282c3a4543c
- **github.com/filebrowser/filebrowser/v2 (go)** <= 2.62.1, fixed in 2.62.2: File Browser is vulnerable to Stored Cross-site Scripting via crafted EPUB file (CVE-2026-34529). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-5vpr-4fgw-f69h.md

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-3xpf-xq7r-v8c5.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-5vpr-4fgw-f69h.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md
