diff --git a/src/main/ipc/browser-session-profile-ipc.test.ts b/src/main/ipc/browser-session-profile-ipc.test.ts index 1b619957411..0ae2a9f5181 100644 --- a/src/main/ipc/browser-session-profile-ipc.test.ts +++ b/src/main/ipc/browser-session-profile-ipc.test.ts @@ -1,13 +1,25 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -const { handleMock, removeHandlerMock, createProfileMock, routeIdentityMock, detectBrowsersMock } = - vi.hoisted(() => ({ - handleMock: vi.fn(), - removeHandlerMock: vi.fn(), - createProfileMock: vi.fn(), - routeIdentityMock: vi.fn(), - detectBrowsersMock: vi.fn(() => []) - })) +const { + handleMock, + removeHandlerMock, + createProfileMock, + routeIdentityMock, + detectBrowsersMock, + setBrowserIdentityModeMock +} = vi.hoisted(() => ({ + handleMock: vi.fn(), + removeHandlerMock: vi.fn(), + createProfileMock: vi.fn(), + routeIdentityMock: vi.fn(), + detectBrowsersMock: vi.fn(() => []), + setBrowserIdentityModeMock: vi.fn(async () => ({ ok: true })) +})) + +vi.mock('../browser/browser-identity-mode-store', () => ({ + setBrowserIdentityMode: setBrowserIdentityModeMock, + getBrowserIdentityModeStatus: vi.fn(() => ({ identity: {}, migrationNotice: null })) +})) vi.mock('electron', () => ({ BrowserWindow: { fromWebContents: vi.fn() }, @@ -51,6 +63,8 @@ describe('browser session profile IPC', () => { routeIdentityMock.mockReset() detectBrowsersMock.mockReset() detectBrowsersMock.mockReturnValue([]) + setBrowserIdentityModeMock.mockReset() + setBrowserIdentityModeMock.mockResolvedValue({ ok: true }) setTrustedBrowserRendererWebContentsId(null) }) @@ -63,6 +77,33 @@ describe('browser session profile IPC', () => { } as Electron.WebContents } + function identitySetHandler(): ( + event: { sender: Electron.WebContents }, + mode: unknown + ) => Promise { + registerBrowserHandlers() + return handleMock.mock.calls.find(([channel]) => channel === 'browser:identity:set')?.[1] + } + + // Why reject rather than coerce: the RPC door validates mode against z.enum(['clean','native']) + // and rejects. Coercing an unrecognized value to 'clean' here made one concept answer an unknown + // value two different ways, and reported success for a mode that was quietly replaced. + it('refuses an unrecognized identity mode instead of silently selecting Cleaned', async () => { + setTrustedBrowserRendererWebContentsId(91) + const handler = identitySetHandler() + + await expect(handler({ sender: trustedSender() }, 'rotating')).rejects.toThrow(/rotating/) + expect(setBrowserIdentityModeMock).not.toHaveBeenCalled() + }) + + it('commits a recognized identity mode unchanged', async () => { + setTrustedBrowserRendererWebContentsId(91) + const handler = identitySetHandler() + + await expect(handler({ sender: trustedSender() }, 'native')).resolves.toEqual({ ok: true }) + expect(setBrowserIdentityModeMock).toHaveBeenCalledWith('native') + }) + function clientHostDetectHandler(): ( event: { sender: Electron.WebContents }, args: { environmentId: string } diff --git a/src/main/ipc/browser-session-profile-ipc.ts b/src/main/ipc/browser-session-profile-ipc.ts index 3383abe0040..0d05a079920 100644 --- a/src/main/ipc/browser-session-profile-ipc.ts +++ b/src/main/ipc/browser-session-profile-ipc.ts @@ -20,7 +20,6 @@ import { getBrowserIdentityModeStatus, setBrowserIdentityMode } from '../browser/browser-identity-mode-store' -import { normalizeBrowserUserAgentMode } from '../../shared/browser-user-agent-mode' export function registerBrowserSessionProfileHandlers(): void { ipcMain.removeHandler('browser:session:listProfiles') @@ -62,7 +61,14 @@ export function registerBrowserSessionProfileHandlers(): void { if (!isTrustedBrowserRenderer(event.sender)) { return null } - return setBrowserIdentityMode(normalizeBrowserUserAgentMode(mode)) + // Why reject rather than coerce: the RPC door validates against z.enum(['clean', 'native']) + // and rejects. Coercing an unrecognized value to 'clean' made one concept answer an unknown + // value two different ways, and reported success for a mode that was quietly replaced — + // silently downgrading a future mode name the caller believed was honoured. + if (mode !== 'clean' && mode !== 'native') { + throw new Error(`Unsupported browser identity mode: ${String(mode)}`) + } + return setBrowserIdentityMode(mode) }) ipcMain.handle( diff --git a/src/shared/browser-user-agent-mode.ts b/src/shared/browser-user-agent-mode.ts index 32a82c87cca..79cc26b6845 100644 --- a/src/shared/browser-user-agent-mode.ts +++ b/src/shared/browser-user-agent-mode.ts @@ -36,7 +36,3 @@ export type BrowserIdentityModeSetResult = } identity: BrowserIdentityModeSnapshot } - -export function normalizeBrowserUserAgentMode(mode: unknown): BrowserUserAgentMode { - return mode === 'native' ? 'native' : 'clean' -}