Docs · 10 / 13

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.cancel does 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.
  • srcdoc mode 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.