diff --git a/apps/desktop/src/main/config.test.ts b/apps/desktop/src/main/config.test.ts index bd7d1b86929..b7bba066697 100644 --- a/apps/desktop/src/main/config.test.ts +++ b/apps/desktop/src/main/config.test.ts @@ -3,6 +3,7 @@ import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { describe, expect, it } from 'vitest' import { + addressSpaceOverrideForOrigin, canonicalOrigin, createConfigStore, DEFAULT_ORIGIN, @@ -42,6 +43,17 @@ describe('partitionForOrigin', () => { }) }) +describe('addressSpaceOverrideForOrigin', () => { + it('marks only self-hosted deployment hosts as public', () => { + expect(addressSpaceOverrideForOrigin('https://sim.internal.example')).toBe( + 'sim.internal.example=public' + ) + expect(addressSpaceOverrideForOrigin('https://192.168.50.173')).toBe('192.168.50.173=public') + expect(addressSpaceOverrideForOrigin('https://www.sim.ai')).toBeNull() + expect(addressSpaceOverrideForOrigin('http://localhost:3000')).toBeNull() + }) +}) + describe('canonicalOrigin', () => { it('keeps a rewritten install on its existing cookie partition', () => { // The whole point of rewriting rather than leaving the apex in place: the diff --git a/apps/desktop/src/main/config.ts b/apps/desktop/src/main/config.ts index 28ca953f16f..f4ebf2eeed9 100644 --- a/apps/desktop/src/main/config.ts +++ b/apps/desktop/src/main/config.ts @@ -203,6 +203,24 @@ export function isSimCloudOrigin(origin: string): boolean { } } +/** + * Chromium's Local Network Access checks reject a top-level navigation when a + * self-hosted deployment name resolves to RFC1918 space, before Electron can + * show a permission prompt or the request reaches the server. The configured + * Sim origin is an explicit trust decision and already owns the shell's preload + * bridge, so classify only that hostname as public instead of disabling the + * process-wide checks that protect arbitrary browser tabs. + */ +export function addressSpaceOverrideForOrigin(origin: string): string | null { + try { + const host = new URL(origin).hostname.toLowerCase() + if (isLoopbackHostname(host) || isSimCloudOrigin(origin)) return null + return `${host}=public` + } catch { + return null + } +} + /** * Maps a server origin to its cookie/storage partition. Each origin gets an * isolated persistent partition so sessions never leak across instances. diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts index a0efc04faf5..34f22869713 100644 --- a/apps/desktop/src/main/index.ts +++ b/apps/desktop/src/main/index.ts @@ -61,6 +61,7 @@ import { NativeComputerUseClient } from '@/main/computer-use/native-client' import { ComputerUseService } from '@/main/computer-use/service' import { APP_NAME_FOR_CHANNEL, + addressSpaceOverrideForOrigin, channelForOrigin, createConfigStore, DEFAULT_ORIGIN, @@ -156,6 +157,10 @@ function main(): void { logger.error('Could not repair desktop settings for account-data recovery') } } + const addressSpaceOverride = addressSpaceOverrideForOrigin(config.getOrigin()) + if (addressSpaceOverride) { + app.commandLine.appendSwitch('ip-address-space-overrides', addressSpaceOverride) + } const accountDataAvailable = () => config.isPersistenceAvailable() && !isAccountDataTeardownRequired() const events = createEventLog(join(userDataPath, 'logs'))