Skip to content

feat(web-desktop): resolve web clients to a live window for window-scoped IPC - #1313

Closed
user79697 wants to merge 2 commits into
RunMaestro:rcfrom
user79697:fix/web-desktop-bridge-sender
Closed

feat(web-desktop): resolve web clients to a live window for window-scoped IPC#1313
user79697 wants to merge 2 commits into
RunMaestro:rcfrom
user79697:fix/web-desktop-bridge-sender

Conversation

@user79697

@user79697 user79697 commented Jul 26, 2026

Copy link
Copy Markdown

What this does

FAKE_EVENT, the synthetic IpcMainInvokeEvent that web-desktop clients
invoke ipcMain handlers through, defines senderFrame, frameId,
processId and type, but no sender. This adds a lazily-resolved
sender pointing at a live window's webContents.

Why (revised, please read)

This PR was opened as a crash fix. That framing was wrong for this base
branch, and I would rather correct it than let it merge under a stale
description.

The crash I originally hit is real, but it is already handled on rc by
4765eda (2026-07-04), which added to resolveCallingWindow:

// A web client is not a window; resolve to "no window" instead of letting
// BrowserWindow.fromWebContents throw on the missing WebContents.
if (!event?.sender) return undefined;

My installed 0.19.0-RC build predates that commit, which is why I saw
Cannot read properties of undefined (reading 'getOwnerBrowserWindow')
and the remote page hanging on the splash screen. On current rc that
cannot happen.

So what is left is not a crash but a behavioral gap. With no sender,
every window-scoped handler resolves to "unknown caller" and silently
degrades for web clients:

  • windows:getState returns null
  • claiming a freshly-created agent for the calling window is a no-op
  • setPanelState and window naming are no-ops

Giving the bridge a sender makes those work remotely, on the theory
that a web client is a mirror of the desktop and should act as the window
it mirrors.

That directly contradicts the intent stated in 4765eda ("a web
client is not a window"), so this is your call, not mine. If the
degradation is deliberate, close this and I will not object. I have left
the guard in resolveCallingWindow untouched either way, so nothing here
depends on removing it.

Implementation notes

sender is a getter, not a value: the module is evaluated before any
BrowserWindow exists, and the correct answer changes over the app's
lifetime. It still yields undefined when no window is open
(headless/tray), so every handler's existing "unknown caller" path stays
intact.

Per review feedback, the comment no longer claims getAllWindows() is
creation-ordered, since Electron does not document that. It picks a
live window; with a single window open, the common case, that is the main
window. I looked at using getMainWindow instead, but it is dependency
injected at every call site (web-server-factory.ts) and is not
importable from a module-level const.

Testing

Three regression tests: sender resolves to a live window, destroyed
windows are skipped, no-windows still yields undefined. Verified by
mutation (reverting the getter fails two). Full file: 11/11 passing.

Manually verified against a local web server over loopback with a CDP
probe. Zero failed windows:getState responses out of 11,803 successful
bridge.response frames, no getOwnerBrowserWindow in console, and the
DOM renders the real UI. The only remaining failures are pre-existing
fs:readDir EPERM against Windows junction points
(My Music/My Pictures/My Videos), unrelated to this change.

Review feedback addressed

  • Em dashes removed from the production and test comments (flagged by
    CodeRabbit, Greptile, and Codex).
  • getAllWindows() ordering claim dropped (CodeRabbit).

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability for web-initiated window actions by ensuring requests can be associated with an available application window.
    • Prevented errors when the previously active window has been closed or is unavailable.
    • Added safeguards for scenarios where no usable window exists, leaving sender information unset when appropriate.
    • Expanded test coverage to verify sender selection and skipped destroyed windows for bridge invoke handling.

The remote page (web-desktop mirror) loaded its splash screen and hung
there forever, on localhost, LAN IP and cloudflared tunnel alike.

`FAKE_EVENT` — the synthetic IpcMainInvokeEvent that web clients invoke
ipcMain handlers through — defined senderFrame, frameId, processId and
type, but no `sender`. Handlers that resolve their calling window via
`BrowserWindow.fromWebContents(event.sender)` therefore passed
`undefined`, and Electron threw

    Cannot read properties of undefined (reading 'getOwnerBrowserWindow')

`windows:getState` is one of those handlers and web-desktop calls it
during boot, treating the rejection as fatal.

The error is thrown in the main process, serialized over the WebSocket
and rethrown in the browser, so the stack trace points at preload-*.js
and the identifier appears nowhere in the bundle — which is why this
looked like a renderer or network problem.

Resolve `sender` to the oldest live BrowserWindow's webContents. A web
client mirrors the desktop, so that is the window it should act as. It
is a getter because the module is evaluated before any window exists and
the answer changes over the app's lifetime; it still yields `undefined`
when no window is open, leaving each handler's existing "unknown caller"
path intact.

Covered by three regression tests: sender resolves to a live window,
destroyed windows are skipped, and no-windows still yields undefined.
Verified by mutation — reverting the getter fails two of them.
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Bridge invoke dispatch now provides a synthetic event sender from the oldest live Electron window. Tests mock window enumeration and verify sender behavior for live, destroyed, and absent windows.

Changes

Bridge invoke sender resolution

Layer / File(s) Summary
Synthetic event sender and regression coverage
src/main/web-server/handlers/bridgeHandlers.ts, src/__tests__/main/web-server/handlers/bridgeHandlers.test.ts
FAKE_EVENT.sender resolves the oldest non-destroyed window’s webContents; tests mock BrowserWindow windows, reset state between cases, and cover live, destroyed, and unavailable windows.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: jsydorowicz21, ronaldeddings

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: routing web-desktop IPC to a live window for window-scoped handlers.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 26, 2026

Copy link
Copy Markdown

Greptile Summary

Adds lazy resolution of a live Electron window for synthetic web-desktop IPC events.

  • Supplies the selected window's webContents as event.sender.
  • Skips destroyed windows and preserves undefined when no window is available.
  • Adds regression coverage for live, destroyed, and absent window states.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/main/web-server/handlers/bridgeHandlers.ts Adds a lazy sender getter that resolves a non-destroyed BrowserWindow for web-desktop IPC invocations; the previously reported prohibited dash characters are no longer present.
src/tests/main/web-server/handlers/bridgeHandlers.test.ts Adds focused tests for sender resolution, destroyed-window filtering, and the no-window fallback.

Reviews (2): Last reviewed commit: "docs(web-desktop): correct the event.sen..." | Re-trigger Greptile

Comment thread src/main/web-server/handlers/bridgeHandlers.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2593c7e33

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +51 to +54
* as fatal — the remote page hung on the splash screen forever.
*
* A web client mirrors the desktop UI, so it resolves to the oldest live
* window — `getAllWindows()` is creation-ordered, making that the main

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Replace the prohibited em dashes

The added production comment contains U+2014 on lines 51 and 54, and the added regression-test comment repeats it. Repository guidance explicitly bans this character everywhere, including code comments, so replace all three occurrences with ASCII punctuation before merging.

AGENTS.md reference: AGENTS.md:L163-L169

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/__tests__/main/web-server/handlers/bridgeHandlers.test.ts`:
- Around line 170-174: Update the regression comment around FAKE_EVENT to
replace both em dashes with standard punctuation, preserving the existing
meaning and wording.

In `@src/main/web-server/handlers/bridgeHandlers.ts`:
- Around line 53-56: Replace the getter’s getAllWindows().find() logic with
explicit main-window tracking. Update the relevant window creation and close
handling so the tracked main window is assigned when created and cleared or
reassigned when closed, while preserving the getter’s live resolution behavior.
- Around line 43-62: In the FAKE_EVENT.sender JSDoc comment, replace both em
dashes with permitted punctuation such as periods, commas, or parentheses, while
preserving the explanation and code behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dcdc301d-99ca-45c6-9ab3-22de79894d9b

📥 Commits

Reviewing files that changed from the base of the PR and between c8ed7b3 and e2593c7.

📒 Files selected for processing (2)
  • src/__tests__/main/web-server/handlers/bridgeHandlers.test.ts
  • src/main/web-server/handlers/bridgeHandlers.ts

Comment thread src/__tests__/main/web-server/handlers/bridgeHandlers.test.ts Outdated
Comment thread src/main/web-server/handlers/bridgeHandlers.ts
Comment on lines +53 to +56
* A web client mirrors the desktop UI, so it resolves to the oldest live
* window — `getAllWindows()` is creation-ordered, making that the main
* window. Must be a getter, not a value: this module is evaluated before any
* BrowserWindow exists, and the resolved window changes over the app's life.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does Electron's BrowserWindow.getAllWindows() guarantee creation order in its official API documentation?

💡 Result:

No, the official Electron API documentation does not explicitly guarantee any specific order for the array returned by BrowserWindow.getAllWindows [1][2]. While historically the method has returned windows in the order of their creation [3], this behavior is not documented as a guarantee. Electron maintainers have previously noted that relying on the ordering of this array is not recommended, and developers who require specific ordering—such as z-order or focus order—should manage that state manually within their own application logic [3]. Because it is not an official guarantee, developers should not rely on any specific sequence (e.g., creation order) remaining consistent across future Electron versions [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate files and relevant symbols"
git ls-files | rg '(^|/)bridgeHandlers\.ts$|package\.json$|electron' | head -100

echo
echo "bridgeHandlers excerpt"
file="$(git ls-files | rg '(^|/)bridgeHandlers\.ts$' | head -1)"
if [ -n "${file:-}" ]; then
  wc -l "$file"
  sed -n '1,120p' "$file" | cat -n
fi

echo
echo "Electron version from manifests/scripts"
rg -n '"electron"|electron|`@electron`' package.json package-lock.json pnpm-lock.yaml yarn.lock || true

Repository: RunMaestro/Maestro

Length of output: 17918


Don’t rely on getAllWindows() creation order.

BrowserWindow.getAllWindows() does not document a guaranteed creation order, and Electron has advised relying on explicit app state for z-order/focus/creation order instead. Track the main window explicitly and update that tracking on window close/create rather than using .find() on getAllWindows().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/web-server/handlers/bridgeHandlers.ts` around lines 53 - 56, Replace
the getter’s getAllWindows().find() logic with explicit main-window tracking.
Update the relevant window creation and close handling so the tracked main
window is assigned when created and cleared or reassigned when closed, while
preserving the getter’s live resolution behavior.

Three review bots flagged em dashes in the comments added by the previous
commit; the repo bans them in authored text. Removed from both the
production comment and the regression-test comment.

CodeRabbit also questioned the claim that `getAllWindows()` is
creation-ordered. It is not documented as such by Electron, so the
comment no longer asserts it. The getter picks *a* live window rather
than claiming to pick the main one. `getMainWindow` is dependency
injected everywhere it is used (see web-server-factory.ts) and is not
importable from a module-level const, so `getAllWindows()` remains the
only option here. With a single window open, the common case, the two
are the same window.

Corrected the stated motivation as well. The crash the previous message
described cannot occur on this base: 4765eda (2026-07-04) already
made `resolveCallingWindow` return undefined when `event.sender` is
missing. What remains is a behavioral gap, not a crash. Without a
sender, every window-scoped handler resolves to "unknown caller", so
`windows:getState` returns null and claiming an agent or setting panel
state silently no-ops for web clients. The comments now describe that.

No functional change. Tests unchanged and still passing (11/11).
@user79697 user79697 changed the title fix(web-desktop): give the IPC bridge a real event.sender feat(web-desktop): resolve web clients to a live window for window-scoped IPC Jul 26, 2026
@user79697

Copy link
Copy Markdown
Author

Pushed b7fbe65 addressing the review, and I have retitled and rewritten
the description because my original framing turned out to be wrong.

Review items

  • Em dashes removed from both the production comment and the
    regression-test comment (@coderabbitai, @greptile-apps,
    chatgpt-codex-connector all flagged these).
  • Dropped the claim that getAllWindows() is creation-ordered.
    @coderabbitai was right that Electron does not document any ordering.
    The comment now says it picks a live window rather than asserting it
    picks the main one. I checked whether getMainWindow could be used
    instead: it is dependency injected at every call site
    (web-server-factory.ts:139) and never exported at module level, so it
    is not reachable from a module-level const like FAKE_EVENT.

The part worth your attention

I opened this as a fix for
Cannot read properties of undefined (reading 'getOwnerBrowserWindow').
While addressing the review I found that 4765eda (2026-07-04) already
guards that path:

if (!event?.sender) return undefined;

That commit is an ancestor of both rc and this branch, so the crash I
described cannot occur here. I hit it because the installed 0.19.0-RC
build predates the guard; I confirmed by scanning the shipped app.asar
(resolveCallingWindow present, guard absent).

That makes this a semantics change rather than a crash fix, and it cuts
against the intent your comment states, that a web client is not a
window. What it buys is that window-scoped handlers stop silently
no-oping for web clients: windows:getState returns real state instead
of null, and agent claiming, panel state, and window naming start
working from the remote page.

Since that is a product decision about what a web client is, it is
yours to make. Happy to close this as superseded if the current
degradation is intentional. I deliberately did not touch the guard in
resolveCallingWindow, so this change composes with it rather than
replacing it.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Changed files and latest relevant commit =='
git show --stat --oneline --decorate --no-renames HEAD
git log --oneline -5

printf '%s\n' '== Candidate source files =='
fd -a 'bridgeHandlers\.ts|bridgeHandlers\.test\.ts|web-server-factory\.ts' src || true
rg -n -C 4 --glob '*.ts' \
  'resolveCallingWindow|windows:getState|FAKE_EVENT|handleBridgeInvoke|BrowserWindow\.fromWebContents|getMainWindow' \
  src

Length of output: 211671


@user79697 Thanks for tracking this down so thoroughly. I agree with your revised assessment.

The existing guard in resolveCallingWindow makes the original crash report obsolete on this branch, and the existing senderless-bridge regression test establishes the current contract: a web client is not attributed to a desktop window.

Injecting an arbitrary live window’s webContents changes that contract. In a multi-window session it can cause a remote client to read or mutate whichever window happens to be selected, including agent ownership and per-window panel state. Since the bridge event has no genuine caller window identity, it cannot safely supply one.

I recommend closing this PR as superseded rather than merging it as a fix. If remote window-scoped behavior is desired later, it should be a deliberate design with explicit remote-client/window identity rather than a synthetic event.sender.

🐇🔍


✏️ Learnings added
Learnt from: user79697
URL: https://github.com/RunMaestro/Maestro/pull/1313

Timestamp: 2026-07-26T22:33:31.661Z
Learning: In `src/main/ipc/handlers/windows.ts`, `resolveCallingWindow` intentionally treats web-desktop bridge IPC events without `event.sender` as having no calling window. Window-scoped handlers such as `windows:getState`, `windows:registerSession`, and `windows:setPanelState` must not attribute a web client to an arbitrary `BrowserWindow`; web clients lack a real Electron window identity, and synthesizing one can violate multi-window ownership isolation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@pedramamini

Copy link
Copy Markdown
Collaborator

@user79697 Thanks for the contribution, and genuinely thank you for the follow-up comment. Coming back to say "my original framing was wrong, here is what this actually is, and here is why you might want to close it" is a rare and very welcome thing to see on a PR. You also nailed the review items: em dashes are gone from both comments, the getAllWindows() ordering claim is dropped, and I pulled the branch and confirmed 11/11 passing locally. No merge conflicts either.

To answer your direct question first: yes, the gap you found is real. windows:getState returning null to a web client is a genuine papercut, not a designed behavior, and I do not want to leave it there. But I cannot take this implementation, because giving sender to the shared FAKE_EVENT reaches a lot further than the four windows:* handlers you are aiming at.

The blast radius

FAKE_EVENT is the event object for every bridged ipcMain.handle channel, so anything that inspects event.sender changes behavior. Two of those are trust guards that currently reject web clients only because sender is undefined:

1. plugins:request-consent (src/main/index.ts:2209)

// Open the consent window. Only the trusted main renderer may ask.
ipcMain.handle('plugins:request-consent', async (event, pluginId: unknown) => {
	if (event.sender !== mainWindow?.webContents) throw new Error('UntrustedConsentRequester');

Today a bridged call has no sender, the comparison fails, and the call is rejected. With this getter, in the single-window case (the case you correctly call common) getAllWindows().find(...) is mainWindow.webContents, so the guard passes and any token-holding web client can pop a native consent window for an arbitrary plugin id. This guard is load-bearing: there is no plugins:setEnabled channel, consent is the enable path.

The mint itself stays safe, for the record. plugins:confirm-consent runs senderTokenOf(event) through consentMinter.confirm, which checks the token against the consent window's own sender plus the one-time nonce, and a main-window id will not match. So this is not a capability-grant escalation. It is a remote client being able to force a native permission dialog onto the desktop user, through a check whose comment says only the main renderer may ask.

There is a second-order wrinkle here too: the getAllWindows() ordering non-guarantee you and CodeRabbit already discussed now sits inside a security comparison. With a consent window open, sender could resolve to the consent window's webContents. Nondeterminism in an equality check is worse than the same nondeterminism in windows:getState.

2. browser:clearSessionData (src/main/ipc/handlers/browser-session.ts:43)

if (event.sender.getType() !== 'window') { ...reject... }

Currently that throws a TypeError for bridged calls and the bridge returns it as an error. After this change it passes, so a web client can clearStorageData() + clearCache() on a tab partition. That one may well be what we want for web-desktop, but the comment right above it says it validates the sender precisely because the op is destructive, so it should be an explicit decision rather than a side effect.

(fs:startDragOut and the coworking sender checks are ipcMain.on, and the bridge only dispatches _invokeHandlers, so those are not affected. I checked.)

The multi-window mutations

Separately, within windows:* itself, two of the four handlers are mutations with invariants stated in their comments:

  • windows:registerSession - "Resolved from event.sender so a window only ever claims an agent into itself"
  • windows:setPanelState - "Resolved from event.sender so a window only ever writes its own state"

With one window open, resolving a web client to it is harmless. With two or more, a web client creating an agent claims it into whichever window getAllWindows() happens to return first, and a remote panel collapse silently rewrites a desktop window's persisted panel state. Multi-window is the entire reason the registry exists, so that is exactly the configuration where it misbehaves.

The read-only half (windows:getState, windows:getBounds) does not have this problem. Reads answering for the main window are defensible; writes landing on an arbitrary window are not.

What I would take instead

Scope the resolution to the handlers that want it, rather than putting it on the shared event.

The cleanest version: leave FAKE_EVENT without a sender and let resolveCallingWindow in ipc/handlers/windows.ts opt in. The event already carries type: 'bridge', so that function can recognize a bridge event and resolve the main window itself, replacing the current if (!event?.sender) return undefined early-out with a bridge-aware branch. Every other sender guard in the codebase keeps its current behavior, and the change lands in exactly the file whose comment states the policy you are amending.

On reaching the real main window: your point about getMainWindow being dependency-injected at web-server-factory.ts:139 and unreachable from a module-level const is right, but the bridge module already has an injection seam in installWebContentsBridgeHook. Threading getMainWindow through it would beat getAllWindows().find() and would retire the ordering caveat entirely. Either shape works for me.

If you would rather split it, a PR that fixes only the read-only handlers is an easy yes from me, and we can decide the registerSession / setPanelState semantics separately.

Happy to keep this open while you consider which way you want to go - the investigation here is solid and I would like to land the fix, just with a narrower reach.

@pedramamini

Copy link
Copy Markdown
Collaborator

@user79697 Following up on this after a month. Your call to hand the semantics decision back was the right one, and the decision is: the gap is real and should be fixed, but not by giving FAKE_EVENT a sender.

I have implemented the narrower shape from my earlier comment in #1420, so this does not sit open indefinitely.

What went in

resolveCallingWindow takes an allowBridge option that only the two read handlers pass. windows:getState and windows:getBounds now answer a bridge caller with the primary window; registerSession and setPanelState keep degrading, because both document that a window only ever mutates itself.

Two details worth flagging since they came out of your investigation:

  1. The ordering caveat is gone. Rather than getAllWindows()[0], it resolves the registry's isMain entry. You were right that getMainWindow is unreachable from a module-level const, but the registry already knows which window is primary, so neither is needed. The test registers a secondary window first specifically so an order-based lookup would fail it.

  2. FAKE_EVENT staying senderless is what keeps this contained. I verified both guards from my earlier comment are still closed: plugins:request-consent still rejects with UntrustedConsentRequester, and browser:clearSessionData still rejects on event.sender.getType(). The tests also fail if allowBridge is extended to the write handlers, so the narrow scope is pinned rather than merely intended.

Closing this one

Closing as superseded by #1420, not as rejected. You found the gap, characterized it correctly, and then came back to say your own original framing was wrong once you found 4765edae1a - that last part is genuinely rare and it is why the fix is shaped the way it is. You are credited in the PR body and the commit message.

If you would rather have carried it yourself, say so and I will hand it back - the door is open on the registerSession / setPanelState semantics too, which stay unresolved by design.

pedramamini added a commit that referenced this pull request Aug 21, 2026
Web-desktop clients invoke ipcMain through a synthetic event with no sender,
so every window-scoped handler resolved to 'unknown caller': windows:getState
returned null and the remote page booted without the window state it mirrors.

Narrow fix, per the discussion on #1313. resolveCallingWindow takes an
allowBridge option that only the two READ handlers (getState, getBounds) pass.
A bridge event resolves to the registry's isMain entry, not to
BrowserWindow.getAllWindows()[0] - Electron documents no ordering there, and a
nondeterministic answer would be read as the caller's identity.

The writes deliberately keep degrading. registerSession and setPanelState both
document that a window only ever mutates itself; with two windows open a
remote agent-create would claim the agent into a window the remote user never
chose, and a remote panel collapse would rewrite a desktop window's state.

FAKE_EVENT keeps having no sender, which is what keeps the blast radius to
this file: plugins:request-consent still rejects web clients with
UntrustedConsentRequester, and browser:clearSessionData still rejects them on
event.sender.getType(). Giving the shared event a sender would have opened
both.

Tests pin it from both sides: red against rc without the fix, and red again if
allowBridge is extended to the write handlers.

Supersedes #1313 by user79697, whose investigation found the gap.
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.

2 participants