[P0 security] Browser actors are not sandboxed from origin storage or network APIs #127

Open
opened 2026-07-28 22:54:01 +00:00 by lost-rob0t · 0 comments
lost-rob0t commented 2026-07-28 22:54:01 +00:00 (Migrated from github.com)

Finding

src/lib/browser-actor-runtime.js executes manifest-provided JavaScript inside a same-origin Worker created from a Blob. Removing a few globals (fetch, XMLHttpRequest, WebSocket, EventSource, importScripts) does not create a security boundary.

Actor code can still reach browser primitives that bypass the declared capability broker, including same-origin storage and additional execution/network surfaces such as indexedDB, caches, BroadcastChannel, nested Worker, dynamic import(), WebRTC-related APIs where available, and any future worker global added by the platform. Because the worker shares the application origin, a malicious imported actor can read or mutate Quasar state and potentially exfiltrate it.

The current UI and architecture describe actors as capability-restricted. That guarantee is false with a same-origin worker.

Impact

  • Imported actor manifests can bypass capabilities.
  • Quasar corpus, settings, provider configuration, and application state may be exposed or modified.
  • Blocking a hand-picked list of globals will regress whenever browsers add APIs.

Required fix

Treat browser actor source as untrusted and move execution to an opaque-origin sandbox. A worker can remain an optimization only for code that is explicitly trusted/signed.

Recommended shape:

const sandbox = document.createElement("iframe");
sandbox.sandbox = "allow-scripts"; // deliberately omit allow-same-origin
sandbox.hidden = true;
sandbox.srcdoc = `<!doctype html>
<meta http-equiv="Content-Security-Policy"
  content="default-src 'none'; script-src 'unsafe-inline'; connect-src 'none';
           img-src 'none'; media-src 'none'; font-src 'none'; style-src 'none';
           frame-src 'none'; worker-src 'none'; object-src 'none'; base-uri 'none'">
<script>
  // Install a narrow postMessage RPC bridge, then evaluate only the actor body.
  // Do not expose parent, origin storage, network, nested workers, or navigation.
<\/script>`;
document.body.append(sandbox);

Parent-side messages must validate all of:

function acceptActorMessage(event, frameWindow, runToken) {
  return event.source === frameWindow
    && event.data?.channel === "quasar-browser-actor"
    && event.data?.runToken === runToken;
}

Also:

  1. Mark existing actors as trusted local code only until the opaque-origin runner lands.
  2. Require an explicit warning/confirmation before enabling or importing unsigned actor source.
  3. Add adversarial tests proving actor code cannot access indexedDB, caches, nested workers, dynamic imports, or direct network primitives.
  4. Document that capability enforcement is a security boundary only in the isolated runner.

Acceptance criteria

  • Untrusted actor source executes in an opaque origin.
  • CSP denies all direct network and child execution paths.
  • The only data path is validated parent RPC.
  • Actor tests attempt known bypasses and fail closed.
  • No same-origin worker path is used for unsigned/untrusted actor manifests.
## Finding `src/lib/browser-actor-runtime.js` executes manifest-provided JavaScript inside a same-origin `Worker` created from a Blob. Removing a few globals (`fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `importScripts`) does **not** create a security boundary. Actor code can still reach browser primitives that bypass the declared capability broker, including same-origin storage and additional execution/network surfaces such as `indexedDB`, `caches`, `BroadcastChannel`, nested `Worker`, dynamic `import()`, WebRTC-related APIs where available, and any future worker global added by the platform. Because the worker shares the application origin, a malicious imported actor can read or mutate Quasar state and potentially exfiltrate it. The current UI and architecture describe actors as capability-restricted. That guarantee is false with a same-origin worker. ## Impact - Imported actor manifests can bypass `capabilities`. - Quasar corpus, settings, provider configuration, and application state may be exposed or modified. - Blocking a hand-picked list of globals will regress whenever browsers add APIs. ## Required fix Treat browser actor source as untrusted and move execution to an opaque-origin sandbox. A worker can remain an optimization only for code that is explicitly trusted/signed. Recommended shape: ```jsx const sandbox = document.createElement("iframe"); sandbox.sandbox = "allow-scripts"; // deliberately omit allow-same-origin sandbox.hidden = true; sandbox.srcdoc = `<!doctype html> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; connect-src 'none'; img-src 'none'; media-src 'none'; font-src 'none'; style-src 'none'; frame-src 'none'; worker-src 'none'; object-src 'none'; base-uri 'none'"> <script> // Install a narrow postMessage RPC bridge, then evaluate only the actor body. // Do not expose parent, origin storage, network, nested workers, or navigation. <\/script>`; document.body.append(sandbox); ``` Parent-side messages must validate all of: ```js function acceptActorMessage(event, frameWindow, runToken) { return event.source === frameWindow && event.data?.channel === "quasar-browser-actor" && event.data?.runToken === runToken; } ``` Also: 1. Mark existing actors as **trusted local code only** until the opaque-origin runner lands. 2. Require an explicit warning/confirmation before enabling or importing unsigned actor source. 3. Add adversarial tests proving actor code cannot access `indexedDB`, `caches`, nested workers, dynamic imports, or direct network primitives. 4. Document that capability enforcement is a security boundary only in the isolated runner. ## Acceptance criteria - Untrusted actor source executes in an opaque origin. - CSP denies all direct network and child execution paths. - The only data path is validated parent RPC. - Actor tests attempt known bypasses and fail closed. - No same-origin worker path is used for unsigned/untrusted actor manifests.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/quasar-ui#127
No description provided.