Skip to content

bridge: opt-in browser registration and approval for the Cloud Bridge - #3122

Merged
esokullu merged 4 commits into
webbrain-one:mainfrom
mitchou10:feat/cloud-bridge-browser-approval
Oct 2, 2026
Merged

esokullu merged 4 commits into
webbrain-one:mainfrom
mitchou10:feat/cloud-bridge-browser-approval

Conversation

@mitchou10

Copy link
Copy Markdown
Contributor

Adds an opt-in approval handshake so a backend must approve a browser before cloud_* commands run. This closes the "any connecting socket is trusted" gap.

Refs #3121 (design discussion; the open questions there are still open, so this PR is meant as a concrete proposal).

Protocol (unchanged cloud_* payloads)

extension -> hello {auth, browserId, installationId, browser, extensionVersion, platform, capabilities}
backend   -> connection_pending | connection_approved | connection_rejected

Before approval, cloud_* returns connection_not_approved (403) and is never forwarded to the background. Approval is per socket; every reconnect starts pending again.

Opt-in: only active when a token is configured; otherwise behaviour is unchanged (local MCP server untouched).

Changes

  • Chrome: offscreen/cloud-bridge.js, cloud-runs.js (identity from chrome.storage.local), background.js (resync on token/browserId change).
  • Firefox: new cloud-bridge.js running in the background page (no offscreen document in MV2), a cloud-runs.js mirror, temporary API-mutation support in the Firefox agent, background wiring. Two tests that asserted "Firefox has no cloud bridge" were updated.
  • Settings > Cloud Bridge tab in both builds; English copy is shared across locales via cloud-bridge-copy.mjs (one import line per locale file).
  • examples/cloud-bridge-approval-server.mjs (dependency-free), docs/cloud-bridge-browser-approval.md, and a French test guide.

Tests

  • node test/run.js: 2425 pass.
  • npm run test:cloud-bridge-approval: 14 tests (hello identity, pending, approved, rejected, command before/after approval, reconnect, no-token legacy mode, end-to-end against the example server) for both Chrome and Firefox bridges.
  • Manual: real Chromium with the extension loaded, against the example server, including a real cloud_run with permission prompts answered through cloud_respond.
  • Not verified in a real Firefox install (automated Firefox e2e could not start in my environment).

Security notes

The token is the Cloud Bridge credential only (not a provider API key), stored in chrome.storage.local and never included in status. The bridge URL stays localhost-only and the cloud_* action allowlist is unchanged. Permission prompts during a cloud run are still answered by the backend via cloud_respond (see question 3 in #3121).

Enrich the cloud bridge hello with token, browserId, installationId and
browser/extension/platform info. When a token is configured, each new
socket must receive connection_approved before cloud_* commands run;
connection_pending and connection_rejected are handled. Without a token
the legacy local behaviour is unchanged.

Add a Settings > Cloud Bridge tab (URL, token, browser name, test),
a local example server, tests and docs (EN + FR guide).
Firefox has no offscreen document, so the bridge runs in the background
page (src/firefox/src/cloud-bridge.js) with the same protocol as Chrome.
Add the cloud-runs controller, cloud_* background actions, temporary API
mutation support in the Firefox agent, the Cloud Bridge settings tab and
shared English copy for all locales.

Update the lineage test that asserted Firefox had no cloud-runs module,
add Firefox bridge tests, and document Firefox setup.
Copilot AI balanced review requested due to automatic review settings October 2, 2026 09:51
@vercel

vercel Bot commented Oct 2, 2026

Copy link
Copy Markdown

@mitchou10 is attempting to deploy a commit to the esokullu's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Pending reconnect timers can reconnect rejected Chrome and Firefox bridge sessions, violating the rejection guarantee.

Review effort: Balanced
Findings: 2 High severity · 1 Low severity

Open (3)
What changed in this PR

Adds opt-in, per-socket browser approval to the Cloud Bridge across Chrome and Firefox.

Changes:

  • Adds authenticated approval gating and reconnect handling.
  • Adds Firefox bridge parity and temporary API-mutation support.
  • Adds settings, documentation, an example server, and automated tests.
File Description
test/​run.js Updates Firefox lineage assertions.
test/​cloud-bridge-approval.mjs Tests approval flows for both browsers.
src/​firefox/​src/​ui/​settings.html Adds Firefox Cloud Bridge settings UI.
src/​firefox/​src/​ui/​settings-cloud-bridge.js Implements Firefox settings behavior.
src/​firefox/​src/​ui/​locales/​zh.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​vi.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​uk.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​tr.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​tl.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​th.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​ru.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​pt.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​pl.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​nl.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​ms.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​ko.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​ja.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​id.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​hi.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​he.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​fr.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​fa.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​es.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​en.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​de.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​cloud-bridge-copy.mjs Defines shared English settings copy.
src/​firefox/​src/​ui/​locales/​bn.js Imports shared bridge copy.
src/​firefox/​src/​ui/​locales/​ar.js Imports shared bridge copy.
src/​firefox/​src/​cloud-runs.js Adds Firefox cloud-run controller.
src/​firefox/​src/​cloud-bridge.js Implements Firefox WebSocket approval bridge.
src/​firefox/​src/​background.js Wires Firefox cloud commands and lifecycle.
src/​firefox/​src/​agent/​agent.js Adds temporary API-mutation authorization.
src/​chrome/​src/​ui/​settings.html Adds Chrome Cloud Bridge settings tab.
src/​chrome/​src/​ui/​settings-cloud-bridge.js Implements Chrome settings behavior.
src/​chrome/​src/​ui/​locales/​zh.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​vi.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​uk.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​tr.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​tl.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​th.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​ru.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​pt.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​pl.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​nl.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​ms.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​ko.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​ja.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​id.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​hi.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​he.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​fr.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​fa.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​es.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​en.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​de.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​cloud-bridge-copy.mjs Defines shared English settings copy.
src/​chrome/​src/​ui/​locales/​bn.js Imports shared bridge copy.
src/​chrome/​src/​ui/​locales/​ar.js Imports shared bridge copy.
src/​chrome/​src/​offscreen/​cloud-bridge.js Adds Chrome approval state and identity handshake.
src/​chrome/​src/​cloud-runs.js Supplies persistent bridge identity.
src/​chrome/​src/​background.js Resynchronizes changed bridge identity.
package.json Registers the approval test suite.
examples/​cloud-bridge-approval-server.mjs Adds a local approval test server.
docs/​cloud-bridge-test-guide.fr.md Documents French end-to-end testing.
docs/​cloud-bridge-browser-approval.md Documents protocol and security behavior.
Files not reviewed (5)
  • src/chrome/src/ui/locales/bn.js: Generated file
  • src/chrome/src/ui/locales/hi.js: Generated file
  • src/chrome/src/ui/locales/ru.js: Generated file
  • src/chrome/src/ui/locales/th.js: Generated file
  • src/chrome/src/ui/locales/uk.js: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/chrome/src/offscreen/cloud-bridge.js
Comment thread src/firefox/src/cloud-bridge.js
Comment thread docs/cloud-bridge-test-guide.fr.md Outdated
Updated cloud-bridge.js files for Chrome and Firefox to clear reconnect timer on rejection. Updated test guide to reflect additional tests.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@esokullu

esokullu commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

The approval tests and main regression suite pass according to the review results, but two lifecycle bugs should be fixed before merging:

  • [P2] Serialize initialization of the persistent bridge identity — Chrome controller, also applicable to the Firefox controller.
    When concurrent bridge starts occur before an installation ID exists, both calls can read an empty value and generate different IDs. This can happen because saving settings triggers syncBridge() while the settings handler also sends cloud_bridge_start. The competing starts cause identity-changing reconnects, and out-of-order completion can leave the connected identity different from the persisted one, breaking backend registration on restart. Use single-flight initialization in both controllers.

  • [P2] Honor temporary grants when revoking the global API setting — Firefox agent.
    In Firefox, if a cloud run has temporary API authorization and the persistent setting is toggled on and then off, setAlwaysAllowApiMutations() still checks only apiAllowedTabs. It therefore appends a trusted NOT ALLOWED message and clears authorization tracking even though the temporary grant keeps API mutations enabled. That contradictory instruction can interrupt an authorized API workflow. Mirror Chrome's effective-permission check (this.isApiMutationsAllowed(tabId)), consistent with the agent-parity requirement.

…ction

- Serialize the read-or-create of the installation id in both cloud-runs
  controllers so concurrent bridge starts share one identity.
- Cancel any pending reconnect timer when the backend rejects a socket
  (Chrome offscreen bridge and Firefox bridge).
- Firefox setAlwaysAllowApiMutations now uses isApiMutationsAllowed so a
  temporary cloud-run grant is not revoked by a contradictory note.
- Add regression tests for each and fix the stale test count in the guide.
@esokullu
esokullu merged commit fdac040 into webbrain-one:main Oct 2, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants