From b979bddf4808a6e1e7d318208b27f67314e1a5d2 Mon Sep 17 00:00:00 2001 From: JunghwanNA <70629228+shaun0927@users.noreply.github.com> Date: Sat, 18 Apr 2026 00:44:34 +0900 Subject: [PATCH] Restore explicit consent boundaries for embedded Browser2 capture permissions Browser2 tabs should not inherit the app-wide media/display-capture auto-approval path. This change keeps the default session behavior intact while narrowing the embedded browser session to clipboard-only permissions and denying automatic display-source selection there. Constraint: Browser2 work is currently flowing through the dev branch and needs a minimal, low-risk patch Rejected: Remove session permission handling entirely | could break existing app-level capture flows outside Browser2 Confidence: high Scope-risk: narrow Reversibility: clean Directive: If Browser2 later needs media or display capture, add an explicit per-origin/user approval flow instead of widening the shared allowlist Tested: pnpm install; pnpm run deps; apps/main npm run build; source-backed permission validation JSON Not-tested: Manual interactive Electron permission prompt UX --- apps/x/apps/main/src/main.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/x/apps/main/src/main.ts b/apps/x/apps/main/src/main.ts index 750c7495..9096e02f 100644 --- a/apps/x/apps/main/src/main.ts +++ b/apps/x/apps/main/src/main.ts @@ -116,21 +116,26 @@ protocol.registerSchemesAsPrivileged([ }, ]); -const ALLOWED_SESSION_PERMISSIONS = new Set(["media", "display-capture", "clipboard-read", "clipboard-sanitized-write"]); +const APP_SESSION_PERMISSIONS = new Set(["media", "display-capture", "clipboard-read", "clipboard-sanitized-write"]); +const BROWSER_SESSION_PERMISSIONS = new Set(["clipboard-read", "clipboard-sanitized-write"]); -function configureSessionPermissions(targetSession: Session): void { +function configureSessionPermissions(targetSession: Session, allowedPermissions: Set): void { targetSession.setPermissionCheckHandler((_webContents, permission) => { - return ALLOWED_SESSION_PERMISSIONS.has(permission); + return allowedPermissions.has(permission); }); targetSession.setPermissionRequestHandler((_webContents, permission, callback) => { - callback(ALLOWED_SESSION_PERMISSIONS.has(permission)); + callback(allowedPermissions.has(permission)); }); - // Auto-approve display media requests and route system audio as loopback. - // Electron requires a video source in the callback even if we only want audio. - // We pass the first available screen source; the renderer discards the video track. + // Only sessions that explicitly allow display-capture should receive an + // auto-approved source. Embedded browser tabs use a separate session with a + // narrower permission set. targetSession.setDisplayMediaRequestHandler(async (_request, callback) => { + if (!allowedPermissions.has("display-capture")) { + callback({}); + return; + } const sources = await desktopCapturer.getSources({ types: ['screen'] }); if (sources.length === 0) { callback({}); @@ -159,8 +164,8 @@ function createWindow() { }, }); - configureSessionPermissions(session.defaultSession); - configureSessionPermissions(session.fromPartition(BROWSER_PARTITION)); + configureSessionPermissions(session.defaultSession, APP_SESSION_PERMISSIONS); + configureSessionPermissions(session.fromPartition(BROWSER_PARTITION), BROWSER_SESSION_PERMISSIONS); // Show window when content is ready to prevent blank screen win.once("ready-to-show", () => {