Security model
Every row is one or more tests in test/browser/escape.spec.ts, run in
Chromium, Firefox and WebKit on every change. The list is the contract; a
claim without a test is not made.
| Claim | What the tests do |
|---|---|
| No network from the code | fetch, XMLHttpRequest, WebSocket, EventSource, sendBeacon, importScripts, dynamic import(), nested Worker / SharedWorker, WebTransport, fetch reached through the prototype chain, new Function("return fetch"), indirect eval: each ends as an error, and the fixture server never receives a request. |
| No DOM, no host page | window, document, parent, localStorage, indexedDB, caches, BroadcastChannel are undefined; location is gone, assigning it creates a property and navigates nothing. |
| Only named functions run | A call outside the map runs nothing on the host; the tree has no constructor and a null prototype; function sources are the few-byte wrappers. |
| Nothing but clone crosses | A function or a Proxy in a value → CloneError; File / Blob arrive intact. |
| The channel is private | The code cannot reach the port (typeof port, the own properties of self); self.postMessage reaches a frame that does not listen; the frame accepts one init from its parent and ignores messages without the nonce; the page accepts the handshake only from the frame it created. |
| Loops stop | while (true) {}, a never-resolving promise, memory growth: the run ends at the watchdog or the clock, the worker is gone, the page stayed responsive; signal.abort() ends a run in flight. |
| Caps hold | 21 calls under maxOps: 20 → the 21st ends the run; a 2 KB value under maxOutputBytes: 1000 never arrives; a 2 KB input is refused; 500 log lines under maxLogLines: 50 keep 50 and count 450. |
| Approval cannot be skipped | A marked function under deny never runs; under pause it runs only after approve; under collect it is deferred, observation cuts the run, resume replays recorded results without re-executing, and a call that differs from the approved one is ReplayMismatch. |
| Isolation between runs | A global set in one run is undefined in the next. |
| Host CSP | url mode boots on a page with script-src 'nonce-…' and no 'unsafe-eval'; srcdoc on that page is SandboxUnavailable; on a page whose CSP has no worker-src blob:, srcdoc is SandboxUnavailable with the cause and url works. |
| No fallback | A frame that cannot load (404) is SandboxUnavailable; nothing is compiled in the page. |
What the sandbox does not do:
- It does not make your functions safe. Whatever
orders.canceldoes with the input it receives is your code's responsibility; the sandbox only guarantees the code can call nothing else and that marked calls wait for a human. - It is not a DOM sandbox for your page. Your page is as trusted as it always was; the library runs in it.
srcdocmode inherits your page's CSP (see Frame modes).- It does not hide timing: the code can measure how long a function takes.
Found a hole? See SECURITY.md.