fix(gateway): deny session-scoped clients the broad sessions.subscribe (per-tab isolation)#110184
Conversation
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
Co-authored-by: Cameron Beeley <[email protected]>
A browser-copilot / SESSION_SCOPED_EVENTS connection could call the legacy broad sessions.subscribe and join the flat sessionEventSubscribers set, which server-session-events unions into session.message / sessions.changed recipients. Those events are not in SESSION_SUBSCRIPTION_EVENTS, so they skip the per-tab opt-out filter, delivering every session's live transcript to a connection the per-tab model documents as isolated. Deny the broad path for session-scoped clients; they subscribe per session via sessions.messages.subscribe. Adds a regression test: browser-copilot broad sessions.subscribe is rejected and a foreign session's transcript is not delivered.
f136288 to
fdc8df4
Compare
|
Codex review: needs real behavior proof before merge. Reviewed July 17, 2026, 5:31 PM ET / 21:31 UTC. Summary Reproducibility: no. independently verified high-confidence live reproduction is available in this review. The branch supplies a concrete real-Gateway harness path and identifies the broad-subscriber delivery route, so the condition is source-reproducible but needs inspectable run output. Review metrics: none identified. Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the narrow subscription guard as part of the parent browser-copilot work after a clean rebase, with a redacted real Gateway harness transcript showing that a scoped client cannot receive a foreign session’s live transcript. Do we have a high-confidence way to reproduce the issue? No independently verified high-confidence live reproduction is available in this review. The branch supplies a concrete real-Gateway harness path and identifies the broad-subscriber delivery route, so the condition is source-reproducible but needs inspectable run output. Is this the best way to solve the issue? Yes, conditionally: rejecting the unsafe broad subscription at its ingress point is narrower than altering broadcast filtering and preserves the intended per-session subscription API. The change should first be rebased onto the parent branch’s current contract. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 0546e15c2e81. Label changesLabel changes:
Label justifications:
Evidence reviewedWhat I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
d4af120 to
933db40
Compare
Hardens the per-tab session isolation added in this branch. Follow-up to my review: #109817 (review)
What Problem This Solves
A session-scoped (browser-copilot /
SESSION_SCOPED_EVENTS) connection can call the legacy broadsessions.subscribeand join the flatsessionEventSubscribersset.handleTranscriptUpdateBroadcast(src/gateway/server-session-events.ts:176-184) unions that set intosession.message/sessions.changedrecipients, and those events aren't inSESSION_SUBSCRIPTION_EVENTS(src/gateway/server-broadcast.ts:80), so they skip the per-tab opt-out filter — delivering every session's live transcript to a connection the per-tab model documents as isolated (contra the comment atserver-broadcast.ts:212).Why This Change Was Made
The per-tab boundary is enforced only on the per-session subscription path. A scoped client should never join the broad fanout; it subscribes per session via
sessions.messages.subscribe(which is all the bundled extension ever calls —extensions/browser/chrome-extension/modules/copilot-background.js:374). Denying the broad path for scoped clients closes the gap at the root without touching delivery mechanics.Adding the events to
SESSION_SUBSCRIPTION_EVENTSinstead over-blocks: those broadcasts don't passopts.sessionKeys, so the per-session filter would also drop own-session messages.User Impact
Defense-in-depth. Not drive-by-web-page reachable (the extension only uses the per-session path), but a compromised or malicious browser-copilot connection could otherwise read every session's live transcript rather than only its own tab's. No behavior change for the stock extension.
Evidence
src/gateway/session-message-events.test.ts(browser-copilot broadsessions.subscriberejected + foreignsession.messagenot delivered).session-message-eventssuite stays green (22/22).