test: remove 216 low-value browser network and session tests (#38681)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-05 14:09:12 -08:00
committed by GitHub
co-authored by OrcaWin
parent b9f988be22
commit da0ba0dc70
24 changed files with 65 additions and 4662 deletions
-2
View File
@@ -211,7 +211,6 @@
1 test-no-real-time-wait:src/main/browser/guest-lifecycle/popups/browser-popup-native-compatibility.electron.test.ts
1 test-no-real-time-wait:src/main/browser/identity/browser-session-ua-wire-identity-cross-context.electron.test.ts
1 test-no-real-time-wait:src/main/browser/identity/browser-session-ua-wire-identity.electron.test.ts
1 test-no-real-time-wait:src/main/browser/network/execution-routes/paired-runtime-browser-network-route.test.ts
1 test-no-real-time-wait:src/main/browser/network/tunnels/execution-route-socket-duplex.test.ts
1 test-no-real-time-wait:src/main/browser/network/tunnels/remote-browser-socks-server.test.ts
1 test-no-real-time-wait:src/main/claude-accounts/claude-login-session.test.ts
@@ -663,7 +662,6 @@
2 test-no-wall-clock:src/main/antigravity/posix-hook-invocation.test.ts
2 test-no-wall-clock:src/main/browser/cookies/browser-cookie-import-partition-reland.electron.test.ts
2 test-no-wall-clock:src/main/browser/network/execution-routes/wsl-browser-network-execution-route.test.ts
2 test-no-wall-clock:src/main/browser/sessions/browser-session-registry.test.ts
1 test-no-wall-clock:src/main/claude-accounts/claude-account-folder.test.ts
1 test-no-wall-clock:src/main/claude-usage/persisted-source-authority.test.ts
1 test-no-wall-clock:src/main/claude/claude-background-wake-retention.test.ts
File diff suppressed because one or more lines are too long
@@ -1224,7 +1224,6 @@
"src/main/browser/sessions/browser-session-proxy.test.ts": 857,
"src/main/browser/sessions/browser-session-registry-identity.persistence.test.ts": 603,
"src/main/browser/sessions/browser-session-registry-startup-metadata.test.ts": 478,
"src/main/browser/sessions/browser-session-registry.persistence.test.ts": 1132,
"src/main/browser/sessions/browser-session-registry.test.ts": 883,
"src/main/browser/identity/browser-session-ua-wire-identity-cross-context.electron.test.ts": 7167,
"src/main/browser/identity/browser-session-ua-wire-identity.electron.test.ts": 8280,
@@ -87,69 +87,6 @@ describe('BrowserClientNetworkRouteRegistry', () => {
expect(route.close).toHaveBeenCalledTimes(3)
})
it('rejects native and WSL routes for a different authority runtime', async () => {
const routeFactory = vi.fn(() => createRoute())
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: routeFactory
})
const key = browserNetworkExecutionHostKey({
kind: 'native',
runtimeId: 'runtime-b',
revision: 1
})
await expect(registry.retain(key, signal())).rejects.toThrow(
'browser_client_network_route_authority_mismatch'
)
const wslKey = browserNetworkExecutionHostKey({
kind: 'wsl',
runtimeId: 'runtime-b',
revision: 1,
distro: 'Ubuntu'
})
await expect(registry.retain(wslKey, signal())).rejects.toThrow(
'browser_client_network_route_authority_mismatch'
)
expect(routeFactory).not.toHaveBeenCalled()
})
it('closes every retained route and rejects later admission', async () => {
const firstRoute = createRoute()
const secondRoute = createRoute()
const routeFactory = vi.fn().mockReturnValueOnce(firstRoute).mockReturnValueOnce(secondRoute)
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: routeFactory
})
await registry.retain(
browserNetworkExecutionHostKey({ kind: 'native', runtimeId: 'runtime-a', revision: 1 }),
signal()
)
await registry.retain(
browserNetworkExecutionHostKey({
kind: 'ssh',
targetId: 'ssh-a',
providerEpoch: 'provider-a',
connectionGeneration: 3
}),
signal()
)
await registry.close()
expect(firstRoute.close).toHaveBeenCalledOnce()
expect(secondRoute.close).toHaveBeenCalledOnce()
await expect(
registry.retain(
browserNetworkExecutionHostKey({ kind: 'native', runtimeId: 'runtime-a', revision: 2 }),
signal()
)
).rejects.toThrow('browser_client_network_route_registry_closed')
})
it('retires old authority routes without destroying them before exact page cleanup', async () => {
const route = createRoute()
const registry = new BrowserClientNetworkRouteRegistry({
@@ -181,92 +118,6 @@ describe('BrowserClientNetworkRouteRegistry', () => {
expect(route.close).toHaveBeenCalledOnce()
})
it('force-closes retained retired routes during final shutdown', async () => {
const route = createRoute()
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: () => route
})
await registry.retain(
browserNetworkExecutionHostKey({ kind: 'native', runtimeId: 'runtime-a', revision: 1 }),
signal()
)
void registry.retire()
await registry.close()
expect(route.close).toHaveBeenCalledOnce()
})
it('suspends every retained transport and restores the same listener address', async () => {
const route = createRoute()
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: () => route
})
const key = browserNetworkExecutionHostKey({
kind: 'native',
runtimeId: 'runtime-a',
revision: 1
})
await registry.retain(key, signal())
registry.suspend()
await expect(registry.retain(key, signal())).rejects.toThrow(
'browser_client_network_route_registry_suspended'
)
await registry.reconnect()
expect(route.suspend).toHaveBeenCalledOnce()
expect(route.reconnect).toHaveBeenCalledOnce()
await expect(registry.retain(key, signal())).resolves.toMatchObject({
proxyEndpoint: { host: '127.0.0.1', port: 43123 }
})
await registry.close()
})
it('retries one flaky route without closing healthy retained routes', async () => {
vi.useFakeTimers()
const flaky = createRoute()
flaky.reconnect
.mockRejectedValueOnce(new Error('transient tunnel failure'))
.mockResolvedValue({ host: '127.0.0.1', port: 43123 })
const healthy = createRoute()
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
reconnectGraceMs: 1_000,
reconnectRetryDelayMs: 10,
createRoute: vi.fn().mockReturnValueOnce(flaky).mockReturnValueOnce(healthy)
})
await registry.retain(
browserNetworkExecutionHostKey({ kind: 'native', runtimeId: 'runtime-a', revision: 1 }),
signal()
)
await registry.retain(
browserNetworkExecutionHostKey({
kind: 'ssh',
targetId: 'ssh-a',
providerEpoch: 'provider-a',
connectionGeneration: 1
}),
signal()
)
registry.suspend()
const reconnecting = registry.reconnect()
await vi.runAllTimersAsync()
await expect(reconnecting).resolves.toBeUndefined()
expect(flaky.reconnect).toHaveBeenCalledTimes(2)
expect(healthy.reconnect).toHaveBeenCalledOnce()
expect(flaky.close).not.toHaveBeenCalled()
expect(healthy.close).not.toHaveBeenCalled()
await registry.close()
})
it('aborts a stale route recovery without retaining its retry timer', async () => {
vi.useFakeTimers()
let rejectReconnect = (_error: Error): void => {}
@@ -398,22 +249,6 @@ describe('BrowserClientNetworkRouteRegistry', () => {
expect(retired).toBe(true)
})
it('refuses a retain whose signal aborted before admission', async () => {
const routeFactory = vi.fn(() => createRoute())
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: routeFactory
})
const controller = new AbortController()
controller.abort()
await expect(registry.retain(nativeKey(1), controller.signal)).rejects.toThrow(
'browser_client_network_route_aborted'
)
expect(routeFactory).not.toHaveBeenCalled()
})
it('rejects a retain the moment its signal aborts mid-startup', async () => {
let resolveStart = (_address: { host: string; port: number }): void => {}
const route = createRoute(
@@ -461,111 +296,6 @@ describe('BrowserClientNetworkRouteRegistry', () => {
expect(route.start).toHaveBeenCalledOnce()
})
it('keeps a fully released key clean so final cleanup closes it only once', async () => {
const route = createRoute()
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: vi.fn(() => route)
})
const retained = await registry.retain(nativeKey(1), signal())
await retained.release()
expect(route.close).toHaveBeenCalledOnce()
await registry.close()
expect(route.close).toHaveBeenCalledOnce()
})
it('never re-closes a route the registry already force-closed', async () => {
const route = createRoute()
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: vi.fn(() => route)
})
const key = nativeKey(1)
const first = await registry.retain(key, signal())
const second = await registry.retain(key, signal())
await registry.close()
await first.release()
await second.release()
expect(route.close).toHaveBeenCalledOnce()
})
it('ignores a repeated release from the same page handle', async () => {
const route = createRoute()
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: vi.fn(() => route)
})
const key = nativeKey(1)
const first = await registry.retain(key, signal())
const second = await registry.retain(key, signal())
await first.release()
await first.release()
expect(route.close).not.toHaveBeenCalled()
await second.release()
expect(route.close).toHaveBeenCalledOnce()
})
it('refuses a retain whose route came back on a different port', async () => {
const route = createRoute()
route.reconnect.mockResolvedValue({ host: '127.0.0.1', port: 43999 })
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: vi.fn(() => route)
})
const key = nativeKey(1)
await registry.retain(key, signal())
await expect(registry.retain(key, signal())).rejects.toThrow(
'browser_client_network_route_address_changed'
)
})
it('refuses a reconnect that moved a retained route to a different port', async () => {
const route = createRoute()
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: vi.fn(() => route)
})
await registry.retain(nativeKey(1), signal())
registry.suspend()
route.reconnect.mockResolvedValue({ host: '127.0.0.1', port: 43999 })
await expect(registry.reconnect()).rejects.toThrow(
'browser_client_network_route_address_changed'
)
})
it('fails retirement when a route refuses to suspend', async () => {
const route = createRoute()
route.suspend.mockImplementation(() => {
throw new Error('suspend refused')
})
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: vi.fn(() => route)
})
await registry.retain(nativeKey(1), signal())
expect(() => registry.retire()).toThrow('Browser client network route suspension failed')
await expect(registry.retire()).rejects.toThrow(
'Browser client network route suspension failed'
)
expect(route.suspend).toHaveBeenCalledOnce()
})
it('reports a failed final cleanup to an awaiting retirement', async () => {
const route = createRoute()
route.close.mockRejectedValue(new Error('route cleanup outcome unknown'))
@@ -585,40 +315,6 @@ describe('BrowserClientNetworkRouteRegistry', () => {
expect(retirementFailure).toBe('Browser client network route cleanup failed')
})
it('refuses to report retirement for a registry whose close already failed', async () => {
const route = createRoute()
route.close.mockRejectedValue(new Error('route cleanup outcome unknown'))
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: vi.fn(() => route)
})
await registry.retain(nativeKey(1), signal())
await expect(registry.close()).rejects.toThrow('Browser client network route cleanup failed')
await expect(registry.retire()).rejects.toThrow('Browser client network route cleanup failed')
})
it('surfaces both the retain failure and its cleanup failure', async () => {
const route = createRoute()
route.start.mockRejectedValue(new Error('tunnel start failed'))
route.close.mockRejectedValue(new Error('route cleanup outcome unknown'))
const registry = new BrowserClientNetworkRouteRegistry({
authority,
authorityStorageKey,
createRoute: vi.fn(() => route)
})
await expect(registry.retain(nativeKey(1), signal())).rejects.toMatchObject({
message: 'tunnel start failed',
errors: [
expect.objectContaining({ message: 'tunnel start failed' }),
expect.objectContaining({ message: 'route cleanup outcome unknown' })
]
})
})
})
function nativeKey(revision: number): string {
@@ -25,7 +25,6 @@ vi.mock('../../../../shared/remote-runtime/remote-runtime-client', () => ({
import { PairedRuntimeBrowserNetworkRoute } from './paired-runtime-browser-network-route'
import { BrowserNetworkTunnelOutboundMemoryBudgetRegistry } from '../tunnels/browser-network-tunnel-outbound-memory-budget'
import { RemoteBrowserSocksServer } from '../tunnels/remote-browser-socks-server'
const pairing = {
v: 2,
@@ -119,92 +118,6 @@ describe('PairedRuntimeBrowserNetworkRoute', () => {
expect(closeSubscription).toHaveBeenCalledOnce()
})
it('surfaces listener teardown failures', async () => {
vi.spyOn(RemoteBrowserSocksServer.prototype, 'close').mockRejectedValueOnce(
new Error('listener close failed')
)
const route = createRoute()
await expect(route.close()).rejects.toThrow('listener close failed')
})
it('tears down the local route when the runtime closes the tunnel', async () => {
let callbacks: RemoteRuntimeSubscriptionCallbacks | undefined
const closeSubscription = vi.fn()
subscribeRemoteRuntimeRequestMock.mockImplementationOnce(
async (...args: unknown[]): Promise<RemoteRuntimeSubscription> => {
callbacks = args[4] as RemoteRuntimeSubscriptionCallbacks
return {
requestId: 'runtime-closed-subscription',
close: closeSubscription,
sendBinary: () => true
}
}
)
const onError = vi.fn()
const route = createRoute({ onError })
const starting = route.start()
await vi.waitFor(() => expect(callbacks).toBeDefined())
callbacks!.onResponse({
id: 'runtime-closed-subscription',
ok: true,
result: { type: 'ready', tunnelGeneration: 7 },
_meta: { runtimeId: 'runtime-a' }
})
await starting
callbacks!.onResponse({
id: 'runtime-closed-subscription',
ok: true,
result: { type: 'closed', tunnelGeneration: 7 },
_meta: { runtimeId: 'runtime-a' }
})
await vi.waitFor(() => expect(closeSubscription).toHaveBeenCalledOnce())
expect(onError).toHaveBeenCalledWith(
expect.objectContaining({ message: 'Browser network route closed by the runtime' })
)
})
it('finishes failure cleanup when reporting and listener teardown both throw', async () => {
let callbacks: RemoteRuntimeSubscriptionCallbacks | undefined
const closeSubscription = vi.fn()
subscribeRemoteRuntimeRequestMock.mockImplementationOnce(
async (...args: unknown[]): Promise<RemoteRuntimeSubscription> => {
callbacks = args[4] as RemoteRuntimeSubscriptionCallbacks
return {
requestId: 'cleanup-failure-subscription',
close: closeSubscription,
sendBinary: () => true
}
}
)
const closeSocks = vi
.spyOn(RemoteBrowserSocksServer.prototype, 'close')
.mockRejectedValueOnce(new Error('listener close failed'))
const route = createRoute({
onError: () => {
throw new Error('reporting failed')
}
})
const starting = route.start()
await vi.waitFor(() => expect(callbacks).toBeDefined())
callbacks!.onResponse({
id: 'cleanup-failure-subscription',
ok: true,
result: { type: 'ready', tunnelGeneration: 7 },
_meta: { runtimeId: 'runtime-a' }
})
await starting
callbacks!.onClose?.()
await vi.waitFor(() => expect(closeSubscription).toHaveBeenCalledOnce())
expect(closeSocks).not.toHaveBeenCalled()
await expect(route.close()).rejects.toThrow('listener close failed')
expect(closeSocks).toHaveBeenCalledOnce()
})
it('binds and releases the exact browser-host outbound memory lease', async () => {
let callbacks: RemoteRuntimeSubscriptionCallbacks | undefined
let subscriptionOptions: Record<string, unknown> | undefined
@@ -235,14 +148,6 @@ describe('PairedRuntimeBrowserNetworkRoute', () => {
expect(registry.evidence()).toMatchObject({ hosts: 0, leases: 0 })
})
it('opens no subscription when browser-host memory admission is exhausted', async () => {
const registry = new BrowserNetworkTunnelOutboundMemoryBudgetRegistry({ processMaxLeases: 0 })
const route = createRoute({ outboundMemoryBudgetRegistry: registry })
await expect(route.start()).rejects.toThrow('outbound memory admission failed')
expect(subscribeRemoteRuntimeRequestMock).not.toHaveBeenCalled()
})
it('keeps one listener fail-closed and accepts an increasing replacement generation', async () => {
const attempts = mockSubscriptionAttempts()
const route = createRoute()
@@ -274,111 +179,6 @@ describe('PairedRuntimeBrowserNetworkRoute', () => {
await route.close()
})
it('suspends the tunnel immediately while preserving the SOCKS listener', async () => {
const attempts = mockSubscriptionAttempts()
const route = createRoute()
const starting = route.start()
await vi.waitFor(() => expect(attempts).toHaveLength(1))
ready(attempts[0]!, 7)
const address = await starting
route.suspend()
expect(attempts[0]!.close).toHaveBeenCalledOnce()
const offline = await connectSocks(address.host, address.port)
await greetSocks(offline)
offline.write(domainConnectRequest('offline.internal', 443))
expect(Array.from(await readExact(offline, 10))).toEqual([5, 1, 0, 1, 0, 0, 0, 0, 0, 0])
offline.destroy()
const reconnecting = route.reconnect()
await vi.waitFor(() => expect(attempts).toHaveLength(2))
ready(attempts[1]!, 8)
await expect(reconnecting).resolves.toEqual(address)
await route.close()
})
it('reattaches with the exact existing v1 payload and capability pair', async () => {
const attempts = mockSubscriptionAttempts()
const route = createRoute({
subscription: { clientCapabilities: ['existing.optional.v1'] }
})
const starting = route.start()
await vi.waitFor(() => expect(attempts).toHaveLength(1))
ready(attempts[0]!, 7)
await starting
attempts[0]!.callbacks.onClose?.()
const reconnecting = route.reconnect()
await vi.waitFor(() => expect(attempts).toHaveLength(2))
ready(attempts[1]!, 8)
await reconnecting
for (const attempt of attempts) {
expect(attempt.method).toBe('network.browserTunnel')
expect(attempt.params).toEqual({
...lease,
executionHost: { kind: 'native', runtimeId: 'runtime-a', revision: 1 }
})
expect(attempt.options).toMatchObject({
perMessageDeflate: false,
clientCapabilities: [
'existing.optional.v1',
'browser.clientHost.v1',
'network.browserTunnel.v1'
]
})
}
await route.close()
})
it('keeps page-command negotiation off the independent tunnel attach', async () => {
const attempts = mockSubscriptionAttempts()
const commandLease = { ...lease, pageCommandProtocolVersion: 1 }
const route = new PairedRuntimeBrowserNetworkRoute({
connection: pairedRuntimeConnectionFromOffer(pairing),
lease: commandLease,
executionHostRevision: 1
})
const starting = route.start()
await vi.waitFor(() => expect(attempts).toHaveLength(1))
ready(attempts[0]!, 7)
await starting
expect(attempts[0]!.params).toEqual({
...lease,
executionHost: { kind: 'native', runtimeId: 'runtime-a', revision: 1 }
})
await route.close()
})
it('negotiates execution-host routing only for an SSH descriptor', async () => {
const attempts = mockSubscriptionAttempts()
const executionHost = {
kind: 'ssh' as const,
targetId: 'target-a',
providerEpoch: 'provider-epoch-a',
connectionGeneration: 2
}
const route = createRoute({ executionHost })
const starting = route.start()
await vi.waitFor(() => expect(attempts).toHaveLength(1))
ready(attempts[0]!, 7)
await starting
expect(attempts[0]).toMatchObject({
method: 'network.browserTunnel',
params: { ...lease, executionHost },
options: {
clientCapabilities: [
'browser.clientHost.v1',
'network.browserTunnel.v1',
'network.browserTunnel.executionHosts.v1'
]
}
})
await route.close()
})
it('ignores late callbacks from a superseded transport', async () => {
const attempts = mockSubscriptionAttempts()
const route = createRoute()
@@ -413,69 +213,6 @@ describe('PairedRuntimeBrowserNetworkRoute', () => {
await route.close()
})
it('reconnects after a current tunnel protocol failure closes the client', async () => {
const attempts = mockSubscriptionAttempts()
const route = createRoute()
const starting = route.start()
await vi.waitFor(() => expect(attempts).toHaveLength(1))
ready(attempts[0]!, 7)
const address = await starting
attempts[0]!.callbacks.onBinary?.(new Uint8Array([1, 2, 3]))
await vi.waitFor(() => expect(attempts[0]!.close).toHaveBeenCalledOnce())
const reconnecting = route.reconnect()
await vi.waitFor(() => expect(attempts).toHaveLength(2))
ready(attempts[1]!, 8)
await reconnecting
const current = await connectSocks(address.host, address.port)
await greetSocks(current)
current.write(domainConnectRequest('recovered.internal', 443))
await vi.waitFor(() => expect(openFrames(attempts[1]!)).toHaveLength(1))
attempts[1]!.callbacks.onBinary?.(
encodeBrowserNetworkTunnelFrame({
opcode: BrowserNetworkTunnelOpcode.Opened,
tunnelGeneration: 8,
streamId: 1,
payload: new Uint8Array()
})
)
expect(Array.from(await readExact(current, 10))).toEqual([5, 0, 0, 1, 0, 0, 0, 0, 0, 0])
current.destroy()
await route.close()
})
it('rebuilds a retained route after its tunnel transport dies unprompted', async () => {
const attempts = mockSubscriptionAttempts()
const route = createRoute()
const starting = route.start()
await vi.waitFor(() => expect(attempts).toHaveLength(1))
ready(attempts[0]!, 7)
const address = await starting
attempts[0]!.callbacks.onClose?.()
await vi.waitFor(() => expect(attempts).toHaveLength(2))
ready(attempts[1]!, 8)
const recovered = await connectSocks(address.host, address.port)
await greetSocks(recovered)
recovered.write(domainConnectRequest('recovered.internal', 443))
await vi.waitFor(() => expect(openFrames(attempts[1]!)).toHaveLength(1))
attempts[1]!.callbacks.onBinary?.(
encodeBrowserNetworkTunnelFrame({
opcode: BrowserNetworkTunnelOpcode.Opened,
tunnelGeneration: 8,
streamId: 1,
payload: new Uint8Array()
})
)
expect(Array.from(await readExact(recovered, 10))).toEqual([5, 0, 0, 1, 0, 0, 0, 0, 0, 0])
recovered.destroy()
await route.close()
})
it('recovers again after a second transport death', async () => {
const attempts = mockSubscriptionAttempts()
const route = createRoute()
@@ -498,46 +235,6 @@ describe('PairedRuntimeBrowserNetworkRoute', () => {
await route.close()
})
it('retries a failing rebuild and reports the terminal failure to the route owner', async () => {
const attempts = mockSubscriptionAttempts()
const onUnavailable = vi.fn()
const route = createRoute({ onUnavailable, recoveryGraceMs: 200, recoveryRetryDelayMs: 10 })
const starting = route.start()
await vi.waitFor(() => expect(attempts).toHaveLength(1))
ready(attempts[0]!, 7)
await starting
subscribeRemoteRuntimeRequestMock.mockRejectedValue(new Error('runtime unreachable'))
attempts[0]!.callbacks.onClose?.()
await vi.waitFor(() =>
expect(onUnavailable).toHaveBeenCalledWith(
expect.objectContaining({ message: expect.stringContaining('could not be rebuilt') })
)
)
expect(subscribeRemoteRuntimeRequestMock.mock.calls.length).toBeGreaterThan(2)
await route.close()
})
it('abandons transport recovery once the route is suspended', async () => {
const attempts = mockSubscriptionAttempts()
const route = createRoute({ recoveryGraceMs: 5_000 })
const starting = route.start()
await vi.waitFor(() => expect(attempts).toHaveLength(1))
ready(attempts[0]!, 7)
await starting
attempts[0]!.callbacks.onClose?.()
await vi.waitFor(() => expect(attempts).toHaveLength(2))
attempts[1]!.callbacks.onClose?.()
route.suspend()
const settled = attempts.length
await new Promise((resolve) => setTimeout(resolve, 250))
expect(attempts).toHaveLength(settled)
await route.close()
})
it('rejects a non-increasing replacement generation and remains retryable', async () => {
const attempts = mockSubscriptionAttempts()
const route = createRoute()
@@ -1,10 +1,8 @@
import { EventEmitter, once } from 'node:events'
import { EventEmitter } from 'node:events'
import type { ChildProcess } from 'node:child_process'
import { connect, createServer, type AddressInfo, type Socket } from 'node:net'
import { PassThrough } from 'node:stream'
import { connect, createServer, type Socket } from 'node:net'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { SshConnection } from '../../../ssh/connection/ssh-connection'
import type { SshProviderEpoch } from '../../../../shared/ssh-types'
import { resolveSshBrowserNetworkExecutionRoute } from './ssh-browser-network-execution-route'
import { setNetworkProxySettingsSource } from './browser-network-proxy-settings-source'
import type { BrowserNetworkTunnelSocket } from '../tunnels/browser-network-tunnel-stream-state'
@@ -48,75 +46,6 @@ afterEach(async () => {
})
describe('SSH browser network execution route', () => {
it('rejects an unavailable or stale authority before opening a destination', async () => {
const connection = fakeConnection({ forwardOut: vi.fn() })
const registerAuthorityAbort = vi.fn()
await expect(
resolveSshBrowserNetworkExecutionRoute(
{ executionHost, runtimeId: 'runtime-a', runtimeRevision: 1 },
{
connectionManager: { getConnection: () => connection },
isCurrentAuthority: () => false,
registerAuthorityAbort
}
)
).rejects.toThrow('browser_tunnel_execution_host_unavailable')
expect(registerAuthorityAbort).not.toHaveBeenCalled()
})
it('passes the exact domain and port to ssh2 forwardOut and fences rotation', async () => {
const channel = new PassThrough() as PassThrough & { close: ReturnType<typeof vi.fn> }
channel.close = vi.fn(() => channel.destroy())
const forwardOut = vi.fn(
(
_sourceHost: string,
_sourcePort: number,
_host: string,
_port: number,
callback: (error: Error | undefined, channel: PassThrough) => void
) => callback(undefined, channel)
)
const connection = fakeConnection({ forwardOut })
let current = true
let authorityAbort: AbortController | undefined
const removeAuthorityAbort = vi.fn()
const route = await resolveSshBrowserNetworkExecutionRoute(
{ executionHost, runtimeId: 'runtime-a', runtimeRevision: 1 },
{
connectionManager: { getConnection: () => connection },
isCurrentAuthority: (authority) =>
current &&
authority.providerEpoch === ('provider-epoch-a' as SshProviderEpoch) &&
authority.connectionGeneration === 2,
registerAuthorityAbort: (_authority, controller) => {
authorityAbort = controller
return removeAuthorityAbort
},
readHostProxyEnvironment: noHostProxy
}
)
const socket = route.connect({ host: 'split-horizon.internal', port: 8443 })
await once(socket as unknown as EventEmitter, 'connect')
expect(forwardOut).toHaveBeenCalledWith(
'127.0.0.1',
0,
'split-horizon.internal',
8443,
expect.any(Function)
)
current = false
authorityAbort?.abort()
await route.whenInvalidated
expect(route.isValid()).toBe(false)
await route.close()
await route.close()
expect(removeAuthorityAbort).toHaveBeenCalledOnce()
expect(socket.destroyed).toBe(true)
})
it('does not adopt a replacement ssh2 client under the captured authority', async () => {
const firstForwardOut = vi.fn()
const secondForwardOut = vi.fn()
@@ -177,83 +106,6 @@ describe('SSH browser network execution route', () => {
expect(close).toHaveBeenCalledOnce()
})
it('owns one system dynamic forward for the whole execution route', async () => {
const connection = fakeConnection(null)
const process = new EventEmitter() as EventEmitter & {
exitCode: number | null
signalCode: NodeJS.Signals | null
}
process.exitCode = null
process.signalCode = null
const dispose = vi.fn()
const close = vi.fn(async () => {})
const startDynamicForward = vi.fn(async () => ({
localPort: 45678,
process: process as unknown as ChildProcess,
stderrTail: () => '',
dispose,
close
}))
const route = await resolveSshBrowserNetworkExecutionRoute(
{ executionHost, runtimeId: 'runtime-a', runtimeRevision: 1 },
{
connectionManager: { getConnection: () => connection },
isCurrentAuthority: () => true,
registerAuthorityAbort: () => () => {},
startDynamicForward,
readHostProxyEnvironment: noHostProxy
}
)
expect(startDynamicForward).toHaveBeenCalledOnce()
process.emit('error', new Error('dynamic forward failed'))
await route.whenInvalidated
expect(route.isValid()).toBe(false)
await route.close()
await route.close()
expect(dispose).toHaveBeenCalledOnce()
expect(close).toHaveBeenCalledOnce()
})
it('fails a stale system SSH socket loudly instead of closing it silently', async () => {
const listener = createServer()
servers.push(listener)
await new Promise<void>((resolve) => listener.listen(0, '127.0.0.1', resolve))
const connection = fakeConnection(null)
const forwardProcess = new EventEmitter() as EventEmitter & {
exitCode: number | null
signalCode: NodeJS.Signals | null
}
forwardProcess.exitCode = null
forwardProcess.signalCode = null
const route = await resolveSshBrowserNetworkExecutionRoute(
{ executionHost, runtimeId: 'runtime-a', runtimeRevision: 1 },
{
connectionManager: { getConnection: () => connection },
isCurrentAuthority: () => true,
registerAuthorityAbort: () => () => {},
readHostProxyEnvironment: noHostProxy,
startDynamicForward: async () => ({
localPort: (listener.address() as AddressInfo).port,
process: forwardProcess as unknown as ChildProcess,
stderrTail: () => '',
dispose: () => {},
close: async () => {}
})
}
)
forwardProcess.exitCode = 0
const socket = route.connect({ host: 'stale.internal', port: 443 })
const events: string[] = []
socket.on('error', (error) => events.push(`error:${error.message}`))
socket.on('close', () => events.push('close'))
await vi.waitFor(() => expect(events).toContain('close'))
expect(events[0]).toBe('error:browser_tunnel_execution_host_stale')
await route.close()
})
it('aborts system SSH startup when route authorization is revoked', async () => {
const connection = fakeConnection(null)
const controller = new AbortController()
@@ -234,17 +234,6 @@ describe('WSL browser network execution route', () => {
expect(child.kill).toHaveBeenCalledOnce()
})
it('rejects stale runtime identity before launching WSL', async () => {
const launchRelay = vi.fn()
await expect(
resolveWslBrowserNetworkExecutionRoute(
{ executionHost, runtimeId: 'runtime-a', runtimeRevision: 8 },
{ launchRelay }
)
).rejects.toThrow('browser_tunnel_execution_host_mismatch')
expect(launchRelay).not.toHaveBeenCalled()
})
it('invalidates the route when the exact distro process exits', async () => {
const child = createRelayProcess(() => new FakeDestination())
const route = await resolveWslBrowserNetworkExecutionRoute(
@@ -261,26 +250,6 @@ describe('WSL browser network execution route', () => {
await route.close()
})
it('delivers a response larger than the relay receive window', async () => {
const payload = patternedPayload(4 * BROWSER_NETWORK_TUNNEL_INITIAL_WINDOW_BYTES)
const destination = new PacedDestination(payload)
const route = await resolveWslBrowserNetworkExecutionRoute(
{ executionHost, runtimeId: 'runtime-a', runtimeRevision: 7 },
{ launchRelay: async () => createRelayProcess(() => destination.accept()) }
)
const socket = route.connect({ host: 'large.internal', port: 443 })
socket.on('error', () => {})
const received: Buffer[] = []
socket.on('data', (bytes) => received.push(Buffer.from(bytes)))
await vi.waitFor(() => expect(Buffer.concat(received).byteLength).toBe(payload.byteLength), {
timeout: 10_000
})
expect(Buffer.concat(received).equals(payload)).toBe(true)
await route.close()
})
it('holds the WSL destination behind a stalled paired-client reader, then completes', async () => {
const payload = patternedPayload(8 * BROWSER_NETWORK_TUNNEL_INITIAL_WINDOW_BYTES)
const destination = new PacedDestination(payload)
@@ -10,10 +10,7 @@ import {
encodeBrowserNetworkTunnelWindowUpdate,
type BrowserNetworkTunnelFrame
} from '../../../../shared/browser-network-tunnel-protocol'
import {
BROWSER_NETWORK_TUNNEL_CONNECT_TIMEOUT_MS,
BROWSER_NETWORK_TUNNEL_INITIAL_WINDOW_BYTES
} from './browser-network-tunnel-stream-state'
import { BROWSER_NETWORK_TUNNEL_INITIAL_WINDOW_BYTES } from './browser-network-tunnel-stream-state'
import { BrowserNetworkTunnelClient } from './browser-network-tunnel-client'
function frame(
@@ -130,101 +127,6 @@ describe('BrowserNetworkTunnelClient', () => {
client.close()
})
it('destroys every stream when the route closes or rejects a frame', async () => {
const client = new BrowserNetworkTunnelClient({
tunnelGeneration: 7,
sendBinary: () => true
})
const firstOpening = client.open({ host: 'one.internal', port: 80 })
client.handleBinary(frame(BrowserNetworkTunnelOpcode.Opened, new Uint8Array(), 7, 1))
const first = await firstOpening
first.on('error', () => {})
const secondOpening = client.open({ host: 'two.internal', port: 80 })
client.handleBinary(frame(BrowserNetworkTunnelOpcode.Opened, new Uint8Array(), 7, 2))
const second = await secondOpening
second.on('error', () => {})
client.handleBinary(new Uint8Array([1, 2, 3]))
expect(first.destroyed).toBe(true)
expect(second.destroyed).toBe(true)
await expect(client.open({ host: 'three.internal', port: 80 })).rejects.toThrow(
'Browser tunnel is closed'
)
})
it('retires a remotely failed stream without echoing an error or closing the route', async () => {
const sent: Uint8Array<ArrayBufferLike>[] = []
const client = new BrowserNetworkTunnelClient({
tunnelGeneration: 7,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
const opening = client.open({ host: 'refused.internal', port: 443 })
client.handleBinary(frame(BrowserNetworkTunnelOpcode.Opened))
const socket = await opening
const socketError = once(socket, 'error')
sent.length = 0
client.handleBinary(
frame(BrowserNetworkTunnelOpcode.Error, new TextEncoder().encode('destination_error'))
)
await expect(socketError).resolves.toEqual([
expect.objectContaining({ message: 'Browser tunnel destination failed: destination_error' })
])
expect(sent).toEqual([])
const nextOpening = client.open({ host: 'healthy.internal', port: 443 })
expect(decodeBrowserNetworkTunnelFrame(sent[0]!)?.opcode).toBe(BrowserNetworkTunnelOpcode.Open)
client.handleBinary(frame(BrowserNetworkTunnelOpcode.Opened, new Uint8Array(), 7, 2))
const nextSocket = await nextOpening
nextSocket.on('error', () => {})
client.close()
})
it('rejects a destination failure before opening without an unhandled socket error', async () => {
const client = new BrowserNetworkTunnelClient({
tunnelGeneration: 7,
sendBinary: () => true
})
const opening = client.open({ host: 'refused.internal', port: 443 })
client.handleBinary(
frame(BrowserNetworkTunnelOpcode.Error, new TextEncoder().encode('destination_error'))
)
await expect(opening).rejects.toThrow('Browser tunnel destination failed: destination_error')
// Allow a deferred socket 'error' to surface before the test ends.
await new Promise((resolve) => setImmediate(resolve))
client.close()
})
it('fails a never-connected stream on timeout and notifies the remote', async () => {
vi.useFakeTimers()
let opening: Promise<unknown>
const sent: Uint8Array<ArrayBufferLike>[] = []
try {
const client = new BrowserNetworkTunnelClient({
tunnelGeneration: 7,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
opening = client.open({ host: 'unreachable.internal', port: 443 })
sent.length = 0
vi.advanceTimersByTime(BROWSER_NETWORK_TUNNEL_CONNECT_TIMEOUT_MS)
} finally {
vi.useRealTimers()
}
await expect(opening).rejects.toThrow('Browser tunnel destination connect timed out')
expect(decodeBrowserNetworkTunnelFrame(sent[0]!)?.opcode).toBe(BrowserNetworkTunnelOpcode.Error)
await new Promise((resolve) => setImmediate(resolve))
})
it('ignores a retired stream frame but rejects a never-allocated stream ID', async () => {
const client = new BrowserNetworkTunnelClient({
tunnelGeneration: 7,
@@ -285,35 +187,6 @@ describe('BrowserNetworkTunnelClient', () => {
).toEqual([1, 2])
})
it('withholds destination credit until a readable consumer asks for data', async () => {
const sent: Uint8Array<ArrayBufferLike>[] = []
const client = new BrowserNetworkTunnelClient({
tunnelGeneration: 7,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
const opening = client.open({ host: 'localhost', port: 8080 })
client.handleBinary(frame(BrowserNetworkTunnelOpcode.Opened))
const socket = await opening
socket.on('error', () => {})
sent.length = 0
client.handleBinary(frame(BrowserNetworkTunnelOpcode.Data, new Uint8Array([7, 8, 9])))
expect(sent).toEqual([])
const received = once(socket, 'data')
await expect(received).resolves.toEqual([Buffer.from([7, 8, 9])])
expect(sent).toEqual([])
socket.settleRead(3)
await vi.waitFor(() => expect(sent).toHaveLength(1))
expect(decodeBrowserNetworkTunnelFrame(sent[0]!)?.opcode).toBe(
BrowserNetworkTunnelOpcode.WindowUpdate
)
client.close()
})
it('replenishes only bytes settled after the readable consumer takes them', async () => {
const sent: Uint8Array<ArrayBufferLike>[] = []
const client = new BrowserNetworkTunnelClient({
@@ -3,8 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vite
import {
BrowserNetworkTunnelOpcode,
decodeBrowserNetworkTunnelFrame,
decodeBrowserNetworkTunnelWindowUpdate,
encodeBrowserNetworkTunnelFrame
decodeBrowserNetworkTunnelWindowUpdate
} from '../../../../shared/browser-network-tunnel-protocol'
import { BrowserNetworkTunnelClient } from './browser-network-tunnel-client'
import type { BrowserNetworkTunnelDuplex } from './browser-network-tunnel-duplex'
@@ -67,42 +66,6 @@ describe('browser network tunnel conformance', () => {
await teardown(tunnel)
})
it('ends the client stream when the destination closes without a half-close', async () => {
const tunnel = createConformanceTunnel()
const { duplex, destination } = await openStream(tunnel)
const consumer = consumeDuplex(duplex)
destination.emit('data', new Uint8Array([1, 2, 3]))
await settle()
destination.emit('close')
await whenClosed(duplex)
expect(opcodesFor(tunnel, 'sessionToClient', 1).slice(-1)).toEqual(['Close'])
expect(consumer.ended).toBe(true)
expect(consumer.bytes()).toEqual(new Uint8Array([1, 2, 3]))
expect(tunnel.closures).toEqual([])
expect(tunnel.onSessionClose).not.toHaveBeenCalled()
await teardown(tunnel)
})
it('refuses a stream whose destination connect throws without fencing the tunnel', async () => {
const tunnel = createConformanceTunnel()
const healthy = await openStream(tunnel, 'healthy.internal')
tunnel.refuseNextConnect = true
await expect(tunnel.client.open({ host: 'refused.internal', port: 443 })).rejects.toThrow(
'destination_connect_failed'
)
expect(tunnel.closures).toEqual([])
expect(tunnel.onSessionClose).not.toHaveBeenCalled()
expect(healthy.duplex.destroyed).toBe(false)
await expectStreamStillCarriesBytes(healthy)
await teardown(tunnel)
})
it('fails only the pending stream when the destination never connects', async () => {
const tunnel = createConformanceTunnel()
const healthy = await openStream(tunnel, 'healthy.internal')
@@ -120,20 +83,6 @@ describe('browser network tunnel conformance', () => {
await teardown(tunnel)
})
it('reports the session connect timeout as a stream-scoped error', async () => {
// Use raw frames because the client's earlier timeout would mask the session timeout.
const peer = createRawFramePeerSession()
peer.session.handleBinary(openFrame(1, 'silent.internal', 443))
vi.advanceTimersByTime(BROWSER_NETWORK_TUNNEL_CONNECT_TIMEOUT_MS)
expect(peer.errorsFor(1)).toEqual(['destination_connect_timeout'])
expect(peer.onClose).not.toHaveBeenCalled()
expect(vi.getTimerCount()).toBe(0)
peer.session.close()
expect(peer.aggregate.outstanding).toBe(0)
})
it('fails only the erroring stream when the destination breaks mid-transfer', async () => {
const tunnel = createConformanceTunnel()
const healthy = await openStream(tunnel, 'healthy.internal')
@@ -249,24 +198,6 @@ describe('browser network tunnel conformance', () => {
await teardown(tunnel)
})
it('answers a heartbeat from either peer without disturbing streams', async () => {
const tunnel = createConformanceTunnel()
const healthy = await openStream(tunnel, 'healthy.internal')
const nonce = new Uint8Array([4, 2])
tunnel.client.handleBinary(pingFrame(nonce))
tunnel.session.handleBinary(pingFrame(nonce))
await settle()
expect(heartbeatFrames(tunnel, 'clientToSession')).toEqual([['Pong', nonce]])
expect(heartbeatFrames(tunnel, 'sessionToClient')).toEqual([['Pong', nonce]])
expect(tunnel.closures).toEqual([])
expect(tunnel.onSessionClose).not.toHaveBeenCalled()
await expectStreamStillCarriesBytes(healthy)
await teardown(tunnel)
})
it('leaves no timer or budget claim behind when both peers tear down mid-transfer', async () => {
const tunnel = createConformanceTunnel()
const idle = await openStream(tunnel, 'idle.internal')
@@ -442,37 +373,6 @@ function createConformanceTunnel(): ConformanceTunnel {
return tunnel
}
function createRawFramePeerSession() {
const aggregate = createAggregateBudgetProbe()
const frames: RecordedFrame[] = []
const onClose = vi.fn<() => void>()
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: TUNNEL_GENERATION,
connect: () => new FakeDestination(),
sendBinary: (bytes) => {
const frame = decodeBrowserNetworkTunnelFrame(bytes)
if (frame) {
frames.push({ direction: 'sessionToClient', ...frame })
}
return true
},
onClose,
claimAggregateRetainedBytes: aggregate.claim
})
return {
session,
onClose,
aggregate,
errorsFor: (streamId: number) =>
frames
.filter(
(frame) =>
frame.streamId === streamId && frame.opcode === BrowserNetworkTunnelOpcode.Error
)
.map((frame) => new TextDecoder().decode(frame.payload))
}
}
function record(tunnel: ConformanceTunnel, direction: FrameDirection, bytes: Uint8Array): void {
const frame = decodeBrowserNetworkTunnelFrame(bytes)
if (!frame) {
@@ -593,44 +493,6 @@ function windowUpdatesFor(
.map((frame) => decodeBrowserNetworkTunnelWindowUpdate(frame.payload)!)
}
function heartbeatFrames(
tunnel: ConformanceTunnel,
direction: FrameDirection
): [string, Uint8Array][] {
return tunnel.frames
.filter(
(frame) =>
frame.direction === direction &&
(frame.opcode === BrowserNetworkTunnelOpcode.Ping ||
frame.opcode === BrowserNetworkTunnelOpcode.Pong)
)
.map((frame) => [BrowserNetworkTunnelOpcode[frame.opcode]!, new Uint8Array(frame.payload)])
}
function pingFrame(payload: Uint8Array): Uint8Array {
return encodeBrowserNetworkTunnelFrame({
opcode: BrowserNetworkTunnelOpcode.Ping,
tunnelGeneration: TUNNEL_GENERATION,
streamId: 0,
payload
})
}
function openFrame(streamId: number, host: string, port: number): Uint8Array {
const name = new TextEncoder().encode(host)
const payload = new Uint8Array(4 + name.byteLength)
const view = new DataView(payload.buffer)
view.setUint16(0, port, false)
view.setUint16(2, name.byteLength, false)
payload.set(name, 4)
return encodeBrowserNetworkTunnelFrame({
opcode: BrowserNetworkTunnelOpcode.Open,
tunnelGeneration: TUNNEL_GENERATION,
streamId,
payload
})
}
function concatBytes(chunks: readonly Uint8Array<ArrayBufferLike>[]): Uint8Array {
return new Uint8Array(Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))))
}
@@ -6,10 +6,6 @@ import {
resolveNativeBrowserNetworkExecutionRoute
} from '../execution-routes/browser-network-execution-route'
import { setNetworkProxySettingsSource } from '../execution-routes/browser-network-proxy-settings-source'
import {
matchesProxyBypassRules,
selectBrowserNetworkTunnelProxy
} from './browser-network-tunnel-proxy-selection'
import type { BrowserNetworkTunnelSocket } from './browser-network-tunnel-stream-state'
const closers: (() => Promise<void>)[] = []
@@ -101,18 +97,6 @@ describe('native browser tunnel upstream proxy (#29154)', () => {
])
})
it('sends a credential that is not valid percent-encoding as written', async () => {
const proxy = await startConnectProxy(await startEchoServer())
const socket = connectNativeBrowserNetworkTarget(
{ host: 'site.invalid', port: 443 },
{ HTTPS_PROXY: `http://user:p%ss@127.0.0.1:${proxy.port}` }
)
await expect(roundTrip(socket, 'd')).resolves.toBe('echo:d')
expect(proxy.requests[0]?.authorization).toBe(
`Basic ${Buffer.from('user:p%ss').toString('base64')}`
)
})
it("prefers Orca's proxy setting and its bypass list over the environment", async () => {
const proxy = await startConnectProxy(await startEchoServer())
setNetworkProxySettingsSource(() => ({
@@ -127,23 +111,6 @@ describe('native browser tunnel upstream proxy (#29154)', () => {
expect(proxy.requests.map((request) => request.authority)).toEqual(['site.invalid:80'])
})
it('dials NO_PROXY and loopback destinations directly', async () => {
const echoPort = await startEchoServer()
const proxy = await startConnectProxy(echoPort)
const env = { HTTPS_PROXY: `http://127.0.0.1:${proxy.port}`, NO_PROXY: 'example.test' }
const socket = connectNativeBrowserNetworkTarget({ host: '127.0.0.1', port: echoPort }, env)
await expect(roundTrip(socket, 'b')).resolves.toBe('echo:b')
expect(proxy.requests).toEqual([])
expect(
selectBrowserNetworkTunnelProxy({ host: 'a.example.test', port: 443 }, null, env)
).toEqual({ kind: 'direct' })
for (const host of ['localhost.', 'app.localhost.', '::1', '0:0:0:0:0:0:0:1', '127.1.2.3']) {
expect(selectBrowserNetworkTunnelProxy({ host, port: 3000 }, null, env)).toEqual({
kind: 'direct'
})
}
})
it('reports a proxy refusal as a connect failure instead of falling back to direct', async () => {
const proxy = await startConnectProxy(await startEchoServer(), { reject: 403 })
const socket = connectNativeBrowserNetworkTarget(
@@ -152,69 +119,4 @@ describe('native browser tunnel upstream proxy (#29154)', () => {
)
await expect(roundTrip(socket, 'c')).rejects.toThrow('http_proxy_connect_rejected:403')
})
it('refuses a proxy kind it cannot speak rather than bypassing it', () => {
expect(() =>
connectNativeBrowserNetworkTarget(
{ host: 'site.invalid', port: 443 },
{ ALL_PROXY: 'socks4://127.0.0.1:1080' }
)
).toThrow('browser_tunnel_proxy_unsupported')
expect(
selectBrowserNetworkTunnelProxy({ host: 'site.invalid', port: 443 }, null, {
ALL_PROXY: 'socks5://127.0.0.1:1080'
})
).toMatchObject({ kind: 'socks5' })
})
it('dials directly with no proxy configured', () => {
expect(selectBrowserNetworkTunnelProxy({ host: 'site.invalid', port: 443 }, null, {})).toEqual({
kind: 'direct'
})
})
})
describe('proxy bypass rules', () => {
it.each([
['*', 'any.host', 443, true, true],
['<local>', 'intranet', 80, true, true],
['<local>', 'intranet.corp', 80, false, false],
['.corp.test', 'a.corp.test', 443, true, true],
['.corp.test', 'corp.test', 443, true, false],
['*.corp.test', 'corp.test', 443, true, false],
['*corp.test', 'corp.test', 443, true, true],
['corp.test', 'deep.a.corp.test', 443, true, false],
['corp.test', 'corp.test', 443, true, true],
['corp.test', 'notcorp.test', 443, false, false],
['corp.test:8443', 'corp.test', 443, false, false],
['corp.test:8443', 'corp.test', 8443, true, true],
['10.0.0.0/8', '10.20.30.40', 443, true, true],
['10.0.0.0/8', '11.0.0.1', 443, false, false],
['[fd00::1]', 'fd00::1', 443, true, true],
['[fd00::1]', 'fd00:0:0:0:0:0:0:1', 443, true, true],
['fd00:0::1', '[FD00::1]', 443, true, true],
['https://corp.test', 'corp.test', 443, true, true],
['https://corp.test', 'corp.test', 80, false, false],
['ftp://corp.test', 'corp.test', 21, false, false],
['fc00::/7', 'fd00::123', 443, true, true],
['fc00::/7', '2001:db8::1', 443, false, false],
['192.168.*', '192.168.4.20', 443, true, true],
['api.*.corp.test', 'api.eu.corp.test', 443, true, true],
['api.*.corp.test', 'web.eu.corp.test', 443, false, false],
['corp.test', 'corp.test.', 443, true, true],
['', '169.254.169.254', 80, true, true],
['<-loopback>', 'localhost', 3000, false, false],
['<-loopback>;<local>', 'localhost', 3000, true, true],
['<local>;<-loopback>', 'localhost', 3000, false, false],
['<local>;<-loopback>', 'intranet', 80, true, true]
])('%s against %s:%d (NO_PROXY %s, Chromium %s)', (rule, host, port, noProxy, chromium) => {
expect(matchesProxyBypassRules({ host, port }, rule, 'no-proxy')).toBe(noProxy)
expect(matchesProxyBypassRules({ host, port }, rule, 'chromium')).toBe(chromium)
})
it('proxies loopback destinations when the bypass list subtracts them', () => {
const settings = { httpProxyUrl: 'http://proxy.test:8080', httpProxyBypassRules: '<-loopback>' }
const target = { host: '127.0.0.1', port: 3000 }
expect(selectBrowserNetworkTunnelProxy(target, settings, {})).toMatchObject({ kind: 'http' })
})
})
@@ -9,10 +9,7 @@ import {
encodeBrowserNetworkTunnelOpen,
encodeBrowserNetworkTunnelWindowUpdate
} from '../../../../shared/browser-network-tunnel-protocol'
import {
BROWSER_NETWORK_TUNNEL_INITIAL_WINDOW_BYTES,
type BrowserNetworkTunnelSocket
} from './browser-network-tunnel-stream-state'
import type { BrowserNetworkTunnelSocket } from './browser-network-tunnel-stream-state'
import { BrowserNetworkTunnelSession } from './browser-network-tunnel-session'
class FakeSocket extends EventEmitter implements BrowserNetworkTunnelSocket {
@@ -65,39 +62,6 @@ function frame(
}
describe('BrowserNetworkTunnelSession', () => {
it('opens the exact remote DNS target and grants bounded receive credit', () => {
const socket = new FakeSocket()
const connect = vi.fn(() => socket)
const sent: Uint8Array<ArrayBufferLike>[] = []
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'split-horizon.internal', port: 443 })
)
)
socket.emit('connect')
expect(connect).toHaveBeenCalledWith({ host: 'split-horizon.internal', port: 443 })
expect(socket.paused).toBe(true)
expect(sent.map(decodeBrowserNetworkTunnelFrame)).toEqual([
expect.objectContaining({ opcode: BrowserNetworkTunnelOpcode.Opened, streamId: 3 }),
expect.objectContaining({ opcode: BrowserNetworkTunnelOpcode.WindowUpdate, streamId: 3 })
])
const credit = decodeBrowserNetworkTunnelFrame(sent[1]!)
expect(credit && decodeBrowserNetworkTunnelWindowUpdate(credit.payload)).toBe(
BROWSER_NETWORK_TUNNEL_INITIAL_WINDOW_BYTES
)
})
it('replenishes client credit only after the destination write settles', () => {
const socket = new FakeSocket()
const sent: Uint8Array<ArrayBufferLike>[] = []
@@ -129,149 +93,6 @@ describe('BrowserNetworkTunnelSession', () => {
expect(update && decodeBrowserNetworkTunnelWindowUpdate(update.payload)).toBe(3)
})
it('does not read destination bytes before the client grants credit', () => {
const socket = new FakeSocket()
const sent: Uint8Array<ArrayBufferLike>[] = []
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect: () => socket,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'localhost', port: 8080 })
)
)
socket.emit('connect')
sent.length = 0
session.handleBinary(
frame(BrowserNetworkTunnelOpcode.WindowUpdate, encodeBrowserNetworkTunnelWindowUpdate(3))
)
socket.emit('data', new Uint8Array([4, 5, 6, 7]))
const first = decodeBrowserNetworkTunnelFrame(sent[0]!)
expect(first?.opcode).toBe(BrowserNetworkTunnelOpcode.Data)
expect(first?.payload).toEqual(new Uint8Array([4, 5, 6]))
expect(socket.paused).toBe(true)
session.handleBinary(
frame(BrowserNetworkTunnelOpcode.WindowUpdate, encodeBrowserNetworkTunnelWindowUpdate(1))
)
const second = decodeBrowserNetworkTunnelFrame(sent[1]!)
expect(second?.payload).toEqual(new Uint8Array([7]))
})
it('fences stale generations and closes every stream on disposal', () => {
const socket = new FakeSocket()
const connect = vi.fn(() => socket)
const sent: Uint8Array<ArrayBufferLike>[] = []
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'stale.internal', port: 80 }),
3,
6
)
)
expect(connect).not.toHaveBeenCalled()
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'current.internal', port: 80 })
)
)
session.close()
expect(socket.destroyed).toBe(true)
})
it('rejects stream reuse without replacing the original destination', () => {
const firstSocket = new FakeSocket()
const secondSocket = new FakeSocket()
const connect = vi.fn().mockReturnValueOnce(firstSocket).mockReturnValueOnce(secondSocket)
const sent: Uint8Array<ArrayBufferLike>[] = []
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
const open = (host: string): void =>
session.handleBinary(
frame(BrowserNetworkTunnelOpcode.Open, encodeBrowserNetworkTunnelOpen({ host, port: 80 }))
)
open('first.internal')
open('second.internal')
expect(connect).toHaveBeenCalledTimes(1)
expect(firstSocket.destroyed).toBe(true)
expect(secondSocket.destroyed).toBe(false)
const error = decodeBrowserNetworkTunnelFrame(sent.at(-1)!)
expect(error?.opcode).toBe(BrowserNetworkTunnelOpcode.Error)
expect(error ? new TextDecoder().decode(error.payload) : '').toBe('stream_id_reused')
})
it('rejects current-generation frames for never-reserved and reused stream IDs', () => {
const socket = new FakeSocket()
const sent: Uint8Array<ArrayBufferLike>[] = []
const connect = vi.fn(() => socket)
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
const open = frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'localhost', port: 80 })
)
session.handleBinary(open)
session.handleBinary(frame(BrowserNetworkTunnelOpcode.Close))
session.handleBinary(open)
expect(connect).toHaveBeenCalledTimes(1)
expect(socket.destroyed).toBe(true)
expect(decodeBrowserNetworkTunnelFrame(sent.at(-1)!)?.opcode).toBe(
BrowserNetworkTunnelOpcode.Error
)
const otherSocket = new FakeSocket()
const unknownSession = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect: () => otherSocket,
sendBinary: () => true
})
unknownSession.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'localhost', port: 80 })
)
)
unknownSession.handleBinary(frame(BrowserNetworkTunnelOpcode.Data, new Uint8Array([1]), 4))
expect(otherSocket.destroyed).toBe(true)
})
it('keeps concurrent streams alive when late data arrives for a retired stream', () => {
const firstSocket = new FakeSocket()
const secondSocket = new FakeSocket()
@@ -304,88 +125,6 @@ describe('BrowserNetworkTunnelSession', () => {
session.close()
})
it('accepts unused out-of-order stream IDs without permitting reuse', () => {
const firstSocket = new FakeSocket()
const secondSocket = new FakeSocket()
const connect = vi.fn().mockReturnValueOnce(firstSocket).mockReturnValueOnce(secondSocket)
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect,
sendBinary: () => true
})
const open = (streamId: number): void =>
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'localhost', port: 80 }),
streamId
)
)
open(5)
session.handleBinary(frame(BrowserNetworkTunnelOpcode.Close, new Uint8Array(), 5))
open(3)
expect(connect).toHaveBeenCalledTimes(2)
expect(secondSocket.destroyed).toBe(false)
})
it('fences invalid half-close transitions and emits destination EOF once', () => {
const socket = new FakeSocket()
const sent: Uint8Array<ArrayBufferLike>[] = []
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect: () => socket,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'localhost', port: 80 })
)
)
socket.emit('connect')
session.handleBinary(frame(BrowserNetworkTunnelOpcode.HalfClose))
session.handleBinary(frame(BrowserNetworkTunnelOpcode.Data, new Uint8Array([1])))
expect(socket.ended).toBe(true)
expect(socket.destroyed).toBe(true)
const responseSocket = new FakeSocket()
const responseFrames: Uint8Array<ArrayBufferLike>[] = []
const responseSession = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect: () => responseSocket,
sendBinary: (bytes) => {
responseFrames.push(bytes)
return true
}
})
responseSession.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'localhost', port: 80 })
)
)
responseSocket.emit('connect')
responseFrames.length = 0
responseSession.handleBinary(
frame(BrowserNetworkTunnelOpcode.WindowUpdate, encodeBrowserNetworkTunnelWindowUpdate(1))
)
responseSocket.emit('data', new Uint8Array([1]))
responseSocket.emit('end')
responseSession.handleBinary(
frame(BrowserNetworkTunnelOpcode.WindowUpdate, encodeBrowserNetworkTunnelWindowUpdate(1))
)
expect(
responseFrames
.map(decodeBrowserNetworkTunnelFrame)
.filter((item) => item?.opcode === BrowserNetworkTunnelOpcode.HalfClose)
).toHaveLength(1)
})
it('flushes credited destination bytes before close', () => {
const socket = new FakeSocket()
const sent: Uint8Array<ArrayBufferLike>[] = []
@@ -425,120 +164,6 @@ describe('BrowserNetworkTunnelSession', () => {
expect(socket.destroyed).toBe(true)
})
it('fails closed on pre-connect credit and synchronous transport failure', () => {
const socket = new FakeSocket()
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect: () => socket,
sendBinary: () => true
})
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'localhost', port: 80 })
)
)
session.handleBinary(
frame(BrowserNetworkTunnelOpcode.WindowUpdate, encodeBrowserNetworkTunnelWindowUpdate(1))
)
expect(socket.destroyed).toBe(true)
const throwingSocket = new FakeSocket()
const throwingSession = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect: () => throwingSocket,
sendBinary: () => {
throw new Error('transport closed')
}
})
throwingSession.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'localhost', port: 80 })
)
)
expect(() => throwingSocket.emit('connect')).not.toThrow()
expect(throwingSocket.destroyed).toBe(true)
})
it('closes a stream that exceeds its receive window', () => {
const socket = new FakeSocket()
const sent: Uint8Array<ArrayBufferLike>[] = []
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect: () => socket,
sendBinary: (bytes) => {
sent.push(bytes)
return true
}
})
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'localhost', port: 80 })
)
)
socket.emit('connect')
sent.length = 0
const chunk = new Uint8Array(64 * 1024)
for (let index = 0; index < 4; index += 1) {
session.handleBinary(frame(BrowserNetworkTunnelOpcode.Data, chunk))
}
session.handleBinary(frame(BrowserNetworkTunnelOpcode.Data, new Uint8Array([1])))
expect(socket.destroyed).toBe(true)
expect(decodeBrowserNetworkTunnelFrame(sent.at(-1)!)?.opcode).toBe(
BrowserNetworkTunnelOpcode.Error
)
})
it('bounds the destination open rate with an injectable monotonic clock', () => {
let now = 1_000
const sockets: FakeSocket[] = []
const connect = vi.fn(() => {
const socket = new FakeSocket()
sockets.push(socket)
return socket
})
const sent: Uint8Array<ArrayBufferLike>[] = []
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect,
sendBinary: (bytes) => {
sent.push(bytes)
return true
},
now: () => now
})
const openAndClose = (streamId: number): void => {
const socketCount = sockets.length
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'burst.internal', port: 443 }),
streamId
)
)
if (sockets.length > socketCount) {
sockets.at(-1)!.emit('connect')
session.handleBinary(frame(BrowserNetworkTunnelOpcode.Close, new Uint8Array(), streamId))
}
}
for (let streamId = 1; streamId <= 1024; streamId += 1) {
openAndClose(streamId)
}
openAndClose(1025)
expect(connect).toHaveBeenCalledTimes(1024)
expect(new TextDecoder().decode(decodeBrowserNetworkTunnelFrame(sent.at(-1)!)?.payload)).toBe(
'open_rate_exceeded'
)
now += 10_001
openAndClose(1026)
expect(connect).toHaveBeenCalledTimes(1025)
})
it('caps aggregate retained destination bytes and releases exact stream accounting', () => {
const sockets: FakeSocket[] = []
const session = new BrowserNetworkTunnelSession({
@@ -620,29 +245,6 @@ describe('BrowserNetworkTunnelSession', () => {
expect(sockets.slice(1, 33).some((socket) => socket.destroyed)).toBe(false)
})
it('bounds unsettled destination write chunks independently of byte credit', () => {
const socket = new FakeSocket()
const session = new BrowserNetworkTunnelSession({
tunnelGeneration: 7,
connect: () => socket,
sendBinary: () => true
})
session.handleBinary(
frame(
BrowserNetworkTunnelOpcode.Open,
encodeBrowserNetworkTunnelOpen({ host: 'tiny-writes.internal', port: 443 })
)
)
socket.emit('connect')
for (let index = 0; index < 257; index += 1) {
session.handleBinary(frame(BrowserNetworkTunnelOpcode.Data, new Uint8Array([index & 0xff])))
}
expect(socket.writes).toHaveLength(256)
expect(socket.destroyed).toBe(true)
})
it('releases queued bytes once when an accepted send closes reentrantly', () => {
const socket = new FakeSocket()
let closeOnData = false
@@ -12,10 +12,7 @@ import {
resolveBrowserRouteGuestPopupOpener
} from './browser-route-guest-popup-ownership'
import { BrowserRouteWebContentsRegistry } from './browser-route-webcontents-registry'
const GESTURE_CLICK_AT = 1_700_000_000_000
const partition = `persist:orca-browser-v1-${'a'.repeat(64)}`
const otherPartition = `persist:orca-browser-v1-${'b'.repeat(64)}`
const page = {
partition,
browserPageId: 'page-a',
@@ -30,174 +27,7 @@ afterEach(() => {
})
describe('route guest OAuth popups', () => {
it('denies window.open without a recognized user gesture', () => {
const { guest } = attachRegisteredGuest()
expect(guest.openWindow('https://accounts.example.com/oauth')).toEqual({ action: 'deny' })
expect(popupMocks.openPopupWithOriginBar).not.toHaveBeenCalled()
})
it('allows one popup per gesture, bound to the opener partition, and never replays it', () => {
const { guest, popups } = attachRegisteredGuest()
guest.emitInput('mouseDown')
const allowed = guest.openWindow('https://accounts.example.com/oauth')
expect(allowed.action).toBe('allow')
expect(allowed.overrideBrowserWindowOptions?.webPreferences?.partition).toBe(partition)
expect(allowed.overrideBrowserWindowOptions?.webPreferences?.sandbox).toBe(true)
const opened = allowed.createWindow?.(popupOptions(createPopupContents()))
expect(opened).not.toBeUndefined()
expect(popups()).toHaveLength(1)
expect(popups()[0]?.loaded).toBe(true)
expect(guest.openWindow('https://accounts.example.com/oauth')).toEqual({ action: 'deny' })
})
// Pin elapsed times independently of the gesture-window constant to catch widening.
it('still honors a gesture 1s after the click', () => {
vi.useFakeTimers({ toFake: ['Date'] })
vi.setSystemTime(GESTURE_CLICK_AT)
const { guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
vi.setSystemTime(GESTURE_CLICK_AT + 1_000)
expect(guest.openWindow('https://accounts.example.com/oauth').action).toBe('allow')
})
it('denies a popup more than 1s after the click that would have authorized it', () => {
vi.useFakeTimers({ toFake: ['Date'] })
vi.setSystemTime(GESTURE_CLICK_AT)
const { guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
vi.setSystemTime(GESTURE_CLICK_AT + 1_001)
expect(guest.openWindow('https://accounts.example.com/oauth')).toEqual({ action: 'deny' })
expect(popupMocks.openPopupWithOriginBar).not.toHaveBeenCalled()
})
it('rejects clock rollback instead of treating a future input as a current gesture', () => {
vi.useFakeTimers({ toFake: ['Date'] })
vi.setSystemTime(GESTURE_CLICK_AT)
const { guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
vi.setSystemTime(GESTURE_CLICK_AT - 1)
expect(guest.openWindow('about:blank')).toEqual({ action: 'deny' })
})
it('stops opening popups at the fourth one even while gestures keep arriving', () => {
const { guest, popups } = attachRegisteredGuest()
for (let index = 0; index < 4; index += 1) {
guest.emitInput('mouseDown')
const allowed = guest.openWindow(`https://accounts.example.com/oauth/${index}`)
expect(allowed.action).toBe('allow')
allowed.createWindow?.(popupOptions(createPopupContents()))
}
expect(popups()).toHaveLength(4)
guest.emitInput('mouseDown')
expect(guest.openWindow('https://accounts.example.com/oauth/overflow')).toEqual({
action: 'deny'
})
expect(popups()).toHaveLength(4)
})
it('tells the owning page every popup it refused, and stays quiet once the page is gone', () => {
const { guest, blockedPopups } = attachRegisteredGuest()
guest.openWindow('https://accounts.example.com/oauth')
guest.emitInput('mouseDown')
guest.openWindow('file:///etc/hosts')
expect(blockedPopups()).toEqual([
{ openerWebContentsId: page.webContentsId, url: 'https://accounts.example.com/oauth' },
{ openerWebContentsId: page.webContentsId, url: 'file:///etc/hosts' }
])
guest.destroy()
guest.openWindow('https://accounts.example.com/oauth')
expect(blockedPopups()).toHaveLength(2)
})
it('applies the WebRTC policy before the popup can load and fails closed when it cannot', () => {
const { guest, popups } = attachRegisteredGuest()
guest.emitInput('mouseDown')
const allowed = guest.openWindow('https://accounts.example.com/oauth')
const contents = createPopupContents()
allowed.createWindow?.(popupOptions(contents))
expect(contents.setWebRTCIPHandlingPolicy).toHaveBeenCalledWith('disable_non_proxied_udp')
expect(popups()[0]?.order).toEqual(['webrtc-policy', 'load'])
guest.emitInput('mouseDown')
const second = guest.openWindow('https://accounts.example.com/oauth')
second.createWindow?.(popupOptions(createPopupContents({ webRtcPolicyThrows: true })))
expect(popups()[1]?.loaded).toBe(false)
expect(popups()[1]?.closed).toBe(true)
})
it('fails closed when the popup contents land outside the opener partition', () => {
const { guest, popups } = attachRegisteredGuest()
guest.emitInput('mouseDown')
const allowed = guest.openWindow('https://accounts.example.com/oauth')
allowed.createWindow?.(popupOptions(createPopupContents({ partition: otherPartition })))
expect(popups()[0]?.loaded).toBe(false)
expect(popups()[0]?.closed).toBe(true)
})
it('refuses non-web destinations even with a real gesture', () => {
const { guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
expect(guest.openWindow('file:///etc/hosts')).toEqual({ action: 'deny' })
for (const url of [
'about:blank?state=x',
'about:srcdoc',
'data:text/html,login',
'javascript:alert(1)'
]) {
guest.emitInput('mouseDown')
expect(guest.openWindow(url)).toEqual({ action: 'deny' })
}
expect(popupMocks.openPopupWithOriginBar).not.toHaveBeenCalled()
})
it.each(['about:blank', 'about:blank#state'])(
'admits a gesture-backed blank bootstrap: %s',
(url) => {
const { guest, popups } = attachRegisteredGuest()
expect(guest.openWindow(url)).toEqual({ action: 'deny' })
guest.emitInput('mouseDown')
const allowed = guest.openWindow(url)
expect(allowed.action).toBe('allow')
allowed.createWindow?.(popupOptions(createPopupContents()))
expect(popups()[0]?.loaded).toBe(true)
expect(guest.openWindow(url)).toEqual({ action: 'deny' })
}
)
it('does not treat the quarantine blank-page exemption as popup authority', () => {
const { registry, guest } = attachGuest()
guest.emitInput('mouseDown')
expect(guest.openWindow('about:blank#state')).toEqual({ action: 'deny' })
registry.registerGuest(page)
guest.emitInput('mouseDown')
expect(guest.openWindow('about:blank')).toEqual({ action: 'deny' })
registry.grantNavigation(page)
const claim = registry.claimGuestLifecycle(page)!
registry.revokeNavigation(claim)
guest.emitInput('mouseDown')
expect(guest.openWindow('about:blank')).toEqual({ action: 'deny' })
})
it('spends a gesture on a denial and rechecks authority before adopting a child', () => {
const { registry, guest, popups } = attachRegisteredGuest()
@@ -212,21 +42,6 @@ describe('route guest OAuth popups', () => {
expect(popups()[0]?.closed).toBe(true)
})
it('holds popups shut until the page is registered and navigation is granted', () => {
const { registry, guest } = attachGuest()
guest.emitInput('mouseDown')
expect(guest.openWindow('https://accounts.example.com/oauth')).toEqual({ action: 'deny' })
registry.registerGuest(page)
guest.emitInput('mouseDown')
expect(guest.openWindow('https://accounts.example.com/oauth')).toEqual({ action: 'deny' })
registry.grantNavigation(page)
guest.emitInput('mouseDown')
expect(guest.openWindow('https://accounts.example.com/oauth').action).toBe('allow')
})
it('closes live popups when the opener page is fenced and reopens them once it is regranted', () => {
const { registry, guest, popups } = attachRegisteredGuest()
guest.emitInput('mouseDown')
@@ -246,43 +61,6 @@ describe('route guest OAuth popups', () => {
expect(guest.openWindow('https://accounts.example.com/oauth').action).toBe('allow')
})
it('closes popups and stops accepting new ones when the opener guest is destroyed', () => {
const { guest, popups } = attachRegisteredGuest()
guest.emitInput('mouseDown')
guest
.openWindow('https://accounts.example.com/oauth')
.createWindow?.(popupOptions(createPopupContents()))
guest.destroy()
expect(popups()[0]?.closed).toBe(true)
guest.emitInput('mouseDown')
expect(guest.openWindow('https://accounts.example.com/oauth')).toEqual({ action: 'deny' })
})
it('owns popups by their opener page and drops ownership when the popup is destroyed', () => {
const { guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
const contents = createPopupContents()
guest.openWindow('https://accounts.example.com/oauth').createWindow?.(popupOptions(contents))
expect(resolveBrowserRouteGuestPopupOpener(contents.id)).toBe(page.webContentsId)
contents.emitDestroyed()
expect(resolveBrowserRouteGuestPopupOpener(contents.id)).toBeNull()
})
it('claims no ownership for a popup that failed the partition fence', () => {
const { guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
const contents = createPopupContents({ partition: otherPartition })
guest.openWindow('https://accounts.example.com/oauth').createWindow?.(popupOptions(contents))
expect(resolveBrowserRouteGuestPopupOpener(contents.id)).toBeNull()
})
it('drops popup ownership when the opener guest goes away', () => {
const { guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
@@ -303,27 +81,6 @@ describe('route guest OAuth popups', () => {
expect(resolveBrowserRouteGuestPopupOpener(descendant.id)).toBeNull()
})
it('gives popup descendants the same gesture-gated handler', () => {
const { guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
const contents = createPopupContents()
guest.openWindow('https://accounts.example.com/oauth').createWindow?.(popupOptions(contents))
expect(contents.openWindow('https://accounts.example.com/step-2')).toEqual({ action: 'deny' })
contents.emitInput('mouseDown')
expect(contents.openWindow('https://accounts.example.com/step-2').action).toBe('allow')
})
it('lets a popup page leave when it asks to confirm', () => {
const { guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
const contents = createPopupContents()
guest.openWindow('https://accounts.example.com/oauth').createWindow?.(popupOptions(contents))
// preventDefault() permits navigation for will-prevent-unload.
expect(contents.navigate('https://accounts.example.com/next', 'will-prevent-unload')).toBe(true)
})
it('guards blank descendants and redirects after the root loses authority', () => {
const { registry, guest } = attachRegisteredGuest()
guest.emitInput('mouseDown')
@@ -339,39 +96,6 @@ describe('route guest OAuth popups', () => {
contents.emitInput('mouseDown')
expect(contents.openWindow('about:blank')).toEqual({ action: 'deny' })
})
it('loads a Cmd/Ctrl+Shift-clicked GET itself but never replays a POST that has no page', () => {
const { guest, blockedPopups } = attachRegisteredGuest()
popupMocks.openPopupWithOriginBar.mockImplementation(() => ({
contentWebContents: createPopupContents(),
close: vi.fn(),
onClosed: vi.fn()
}))
guest.emitInput('mouseDown')
guest.openWindow('https://accounts.example.com/docs').createWindow?.({})
expect(popupMocks.openPopupWithOriginBar).toHaveBeenLastCalledWith(
expect.objectContaining({ loadInitialUrl: true }),
'https://accounts.example.com/docs',
expect.any(Function)
)
expect(blockedPopups()).toEqual([])
guest.emitInput('mouseDown')
guest
.openWindow('https://accounts.example.com/submit', {
contentType: 'application/x-www-form-urlencoded',
data: []
})
.createWindow?.({})
expect(popupMocks.openPopupWithOriginBar).toHaveBeenLastCalledWith(
expect.objectContaining({ loadInitialUrl: false }),
'https://accounts.example.com/submit',
expect.any(Function)
)
expect(blockedPopups()).toEqual([
{ openerWebContentsId: page.webContentsId, url: 'https://accounts.example.com/submit' }
])
})
})
function popupOptions(
@@ -182,103 +182,6 @@ describe('BrowserRouteWebContentsRegistry', () => {
).resolves.toBe(true)
})
it('holds navigation and popups until exact registration and an explicit grant', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
expect(registry.attachGuest(guest.guest)).toBe(true)
const beforeRegistration = navigationEvent()
guest.emit('will-navigate', beforeRegistration, 'https://example.com/')
expect(beforeRegistration.preventDefault).toHaveBeenCalledOnce()
expect(guest.openWindow()).toEqual({ action: 'deny' })
expect(registry.registerGuest({ ...page, rendererWebContentsId: 12 })).toBe(false)
expect(registry.grantNavigation(page)).toBe(false)
expect(registry.registerGuest(page)).toBe(true)
const beforeGrant = navigationEvent()
guest.emit('will-navigate', beforeGrant, 'https://example.com/')
expect(beforeGrant.preventDefault).toHaveBeenCalledOnce()
expect(registry.grantNavigation(page)).toBe(true)
const afterGrant = navigationEvent()
guest.emit('will-navigate', afterGrant, 'https://example.com/')
expect(afterGrant.preventDefault).not.toHaveBeenCalled()
guest.setUrl('https://example.com/')
const laterNavigation = navigationEvent()
guest.emit('will-navigate', laterNavigation, 'https://example.org/')
expect(laterNavigation.preventDefault).not.toHaveBeenCalled()
expect(guest.openWindow()).toEqual({ action: 'deny' })
})
it('revokes only the exact guest lifecycle claim without destroying it', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
const claim = requireLifecycleClaim(registry)
expect(
registry.revokeNavigation({
...claim,
registration: { ...page, pageHostGeneration: page.pageHostGeneration + 1 }
})
).toBe(false)
const afterStaleFence = navigationEvent()
guest.emit('will-navigate', afterStaleFence, 'https://example.com/')
expect(afterStaleFence.preventDefault).not.toHaveBeenCalled()
expect(registry.revokeNavigation(claim)).toBe(true)
const afterExactFence = navigationEvent()
guest.emit('will-navigate', afterExactFence, 'https://example.com/')
expect(afterExactFence.preventDefault).toHaveBeenCalledOnce()
expect(guest.guest.close).not.toHaveBeenCalled()
})
it('loads only a normalized web URL on the exact registered guest', async () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
const claim = requireLifecycleClaim(registry)
await expect(registry.navigateGuest(claim, 'example.com/path')).resolves.toBe(true)
expect(guest.guest.loadURL).toHaveBeenCalledWith('https://example.com/path')
await expect(
registry.navigateGuest(
{ ...claim, registration: { ...page, pageHostGeneration: 8 } },
'https://a.test'
)
).resolves.toBe(false)
await expect(registry.navigateGuest(claim, 'file:///etc/passwd')).resolves.toBe(false)
expect(guest.guest.loadURL).toHaveBeenCalledOnce()
})
it('retires an exact unregistered quarantined guest without touching a replacement', async () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
registry.attachGuest(guest.guest)
expect(registry.claimGuestLifecycle({ ...page, webContentsId: 42 })).toBeNull()
expect(guest.guest.close).not.toHaveBeenCalled()
await expect(
registry.beginGuestRetirement(requireLifecycleClaim(registry))
).resolves.toBeUndefined()
expect(guest.guest.close).toHaveBeenCalledOnce()
})
it('does not join a different generation already retiring on the same guest', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession, closeDestroys: false })
registry.attachGuest(guest.guest)
registry.registerGuest(page)
expect(registry.beginGuestRetirement(requireLifecycleClaim(registry))).not.toBeNull()
expect(registry.claimGuestLifecycle({ ...page, pageHostGeneration: 8 })).toBeNull()
})
it('settles exact guest retirement only after its destroyed acknowledgement', async () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession, closeDestroys: false })
@@ -295,36 +198,6 @@ describe('BrowserRouteWebContentsRegistry', () => {
await expect(retired).resolves.toBeUndefined()
})
it('rejects a guest whose renderer does not own the prepared authority', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession, rendererWebContentsId: 12 })
const siblingClaim = { ...page, rendererWebContentsId: 12 }
expect(registry.attachGuest(guest.guest)).toBe(true)
expect(registry.registerGuest(siblingClaim)).toBe(false)
expect(registry.grantNavigation(siblingClaim)).toBe(false)
})
it('blocks non-proxied WebRTC before admitting a route guest', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
expect(registry.attachGuest(guest.guest)).toBe(true)
expect(guest.guest.setWebRTCIPHandlingPolicy).toHaveBeenCalledWith('disable_non_proxied_udp')
})
it('fails closed when WebRTC route policy cannot be applied', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({
session: routeSession,
webRtcPolicyError: new Error('policy unavailable')
})
expect(registry.attachGuest(guest.guest)).toBe(false)
expect(guest.guest.close).toHaveBeenCalledOnce()
expect(registry.registerGuest(page)).toBe(false)
})
it('keeps policy-rejected guests quarantined through delayed or failed close', () => {
const { registry, routeSession } = createHarness()
for (const rejected of [
@@ -347,68 +220,6 @@ describe('BrowserRouteWebContentsRegistry', () => {
}
})
it('allows blank navigation while quarantined but blocks invalid schemes after a grant', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
registry.attachGuest(guest.guest)
const blank = navigationEvent()
guest.emit('will-navigate', blank, 'about:blank')
expect(blank.preventDefault).not.toHaveBeenCalled()
expect(registry.registerGuest(page)).toBe(true)
expect(registry.grantNavigation(page)).toBe(true)
const localFile = navigationEvent()
guest.emit('will-navigate', localFile, 'file:///etc/passwd')
expect(localFile.preventDefault).toHaveBeenCalledOnce()
})
it('destroys invalid route guests but ignores unrelated browser sessions', () => {
const { registry, routeSession } = createHarness()
const unrelated = createGuest({
session: { marker: 'profile-session' } as unknown as Session
})
expect(registry.attachGuest(unrelated.guest)).toBe(false)
expect(unrelated.guest.close).not.toHaveBeenCalled()
for (const invalid of [
createGuest({ session: routeSession, type: 'window' }),
createGuest({ session: routeSession, url: 'https://example.com/' }),
createGuest({ session: routeSession, rendererWebContentsId: 0 })
]) {
expect(registry.attachGuest(invalid.guest)).toBe(false)
expect(invalid.guest.close).toHaveBeenCalledOnce()
}
const alreadyDestroyed = createGuest({ session: routeSession, destroyed: true })
expect(registry.attachGuest(alreadyDestroyed.guest)).toBe(false)
expect(alreadyDestroyed.guest.close).not.toHaveBeenCalled()
})
it('bounds unregistered route guests and destroys excess attachments', () => {
const { registry, routeSession } = createHarness({ maxGuests: 1 })
const first = createGuest({ session: routeSession })
const second = createGuest({ id: 42, session: routeSession })
expect(registry.attachGuest(first.guest)).toBe(true)
expect(registry.attachGuest(second.guest)).toBe(false)
expect(second.guest.close).toHaveBeenCalledOnce()
})
it('keeps delayed or failed admission closure navigation- and popup-denied', () => {
const { registry, routeSession } = createHarness({ maxGuests: 1 })
registry.attachGuest(createGuest({ session: routeSession }).guest)
for (const rejected of [
createGuest({ id: 42, session: routeSession, closeDestroys: false }),
createGuest({ id: 43, session: routeSession, closeError: new Error('close failed') })
]) {
expect(registry.attachGuest(rejected.guest)).toBe(false)
const navigation = navigationEvent()
rejected.emit('will-navigate', navigation, 'https://example.com/')
expect(navigation.preventDefault).toHaveBeenCalledOnce()
expect(rejected.openWindow()).toEqual({ action: 'deny' })
}
})
it('rejects page conflicts and stale destroyed callbacks cannot retire a replacement', () => {
const { registry, replaceAuthority, routeSession } = createHarness()
const first = createGuest({ session: routeSession })
@@ -429,43 +240,6 @@ describe('BrowserRouteWebContentsRegistry', () => {
expect(first.guest.off).toHaveBeenCalledWith('destroyed', expect.any(Function))
})
it('revokes navigation immediately when the logical page is no longer prepared', () => {
const { registry, routeSession, setPrepared } = createHarness()
const guest = createGuest({ session: routeSession })
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
setPrepared(false)
const afterRelease = navigationEvent()
guest.emit('will-redirect', afterRelease, 'https://example.com/', false, true)
expect(afterRelease.preventDefault).toHaveBeenCalledOnce()
})
it('quarantines a late attachment after its logical page authority is released', () => {
const { registry, routeSession, setPrepared } = createHarness()
setPrepared(false)
const guest = createGuest({ session: routeSession })
expect(registry.attachGuest(guest.guest)).toBe(true)
expect(registry.registerGuest(page)).toBe(false)
const navigation = navigationEvent()
guest.emit('will-navigate', navigation, 'https://example.com/')
expect(navigation.preventDefault).toHaveBeenCalledOnce()
expect(guest.openWindow()).toEqual({ action: 'deny' })
})
it('does not retain mutable registration input as page authority', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
const registration = { ...page }
registry.attachGuest(guest.guest)
expect(registry.registerGuest(registration)).toBe(true)
registration.browserPageId = 'mutated-page'
expect(registry.grantNavigation(page)).toBe(true)
})
it('does not revive a stale guest when the same logical page tuple is prepared again', () => {
const { registry, replaceAuthority, routeSession } = createHarness()
const stale = createGuest({ session: routeSession })
@@ -526,110 +300,6 @@ describe('BrowserRouteWebContentsRegistry', () => {
expect(duplicateRetired).not.toHaveBeenCalled()
})
it('ignores retirement for a different opaque authority', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
expect(
registry.retirePageAuthority({
partition,
browserPageId: page.browserPageId,
pageHostGeneration: page.pageHostGeneration,
rendererWebContentsId: page.rendererWebContentsId,
pageAuthority: Symbol('wrong-authority'),
onRetired: vi.fn()
})
).toBe(true)
expect(guest.guest.close).not.toHaveBeenCalled()
const navigation = navigationEvent()
guest.emit('will-navigate', navigation, 'https://example.com/')
expect(navigation.preventDefault).not.toHaveBeenCalled()
})
it('does not settle retirement when destroyed inspection is unavailable', () => {
const { getPageAuthority, registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession, closeDestroys: false })
const retired = vi.fn()
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
vi.mocked(guest.guest.isDestroyed).mockImplementation(() => {
throw new Error('inspection unavailable')
})
expect(
registry.retirePageAuthority({
partition,
browserPageId: page.browserPageId,
pageHostGeneration: page.pageHostGeneration,
rendererWebContentsId: page.rendererWebContentsId,
pageAuthority: getPageAuthority(),
onRetired: retired
})
).toBe(false)
expect(retired).not.toHaveBeenCalled()
const navigation = navigationEvent()
guest.emit('will-navigate', navigation, 'https://example.com/')
expect(navigation.preventDefault).toHaveBeenCalledOnce()
guest.destroy()
expect(retired).toHaveBeenCalledOnce()
})
it('retires the exact page authority when its guest process exits', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession, closeDestroys: false })
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
guest.emit('render-process-gone', {}, { reason: 'crashed' })
expect(guest.guest.close).toHaveBeenCalledOnce()
expect(registry.grantNavigation(page)).toBe(false)
const navigation = navigationEvent()
guest.emit('will-navigate', navigation, 'https://example.com/')
expect(navigation.preventDefault).toHaveBeenCalledOnce()
})
it('reports one exact availability loss for a crashed guest generation', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession, closeDestroys: false })
const unavailable = vi.fn()
registry.watchPageAvailability(page.browserPageId, unavailable)
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
guest.emit('render-process-gone', {}, { reason: 'crashed' })
guest.destroy()
expect(unavailable).toHaveBeenCalledOnce()
expect(unavailable).toHaveBeenCalledWith(page)
})
it('does not report explicit page retirement as an availability loss', () => {
const { getPageAuthority, registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession, closeDestroys: false })
const unavailable = vi.fn()
registry.watchPageAvailability(page.browserPageId, unavailable)
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
registry.retirePageAuthority({
...page,
pageAuthority: getPageAuthority(),
onRetired: vi.fn()
})
guest.destroy()
expect(unavailable).not.toHaveBeenCalled()
})
it('retires every page owned by a crashed host renderer', () => {
const { registry, retirePreparedPagesOwnedByRenderer, routeSession } = createHarness()
const first = createGuest({ session: routeSession })
@@ -650,59 +320,6 @@ describe('BrowserRouteWebContentsRegistry', () => {
expect(retirePreparedPagesOwnedByRenderer).toHaveBeenCalledWith(page.rendererWebContentsId)
})
it('reports registered pages owned by a crashed host renderer', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession, closeDestroys: false })
const unavailable = vi.fn()
registry.watchPageAvailability(page.browserPageId, unavailable)
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
registry.retireRenderer(page.rendererWebContentsId)
expect(unavailable).toHaveBeenCalledOnce()
expect(unavailable).toHaveBeenCalledWith(page)
})
it('keeps renderer retirement fail-closed when logical-owner cleanup throws', () => {
const { registry, retirePreparedPagesOwnedByRenderer, routeSession } = createHarness()
const guest = createGuest({ session: routeSession, closeDestroys: false })
registry.attachGuest(guest.guest)
retirePreparedPagesOwnedByRenderer.mockImplementation(() => {
throw new Error('owner cleanup unavailable')
})
expect(() => registry.retireRenderer(page.rendererWebContentsId)).not.toThrow()
expect(guest.guest.close).toHaveBeenCalledOnce()
})
it('fails closed when Electron guest inspection races destruction', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
vi.mocked(guest.guest.getURL).mockImplementation(() => {
throw new Error('guest destroyed')
})
expect(() => registry.attachGuest(guest.guest)).not.toThrow()
expect(guest.guest.close).toHaveBeenCalledOnce()
})
it('turns registration-time inspection failures into navigation denial', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
registry.attachGuest(guest.guest)
registry.registerGuest(page)
registry.grantNavigation(page)
vi.mocked(guest.guest.getType).mockImplementation(() => {
throw new Error('guest swapped')
})
const navigation = navigationEvent()
expect(() => guest.emit('will-navigate', navigation, 'https://example.com/')).not.toThrow()
expect(navigation.preventDefault).toHaveBeenCalledOnce()
})
it('keeps a destroyed claim distinct from a reused WebContents identity', async () => {
const { registry, replaceAuthority, routeSession } = createHarness()
const first = createGuest({ session: routeSession })
@@ -727,16 +344,4 @@ describe('BrowserRouteWebContentsRegistry', () => {
await expect(registry.beginGuestRetirement(replacementClaim)).resolves.toBeUndefined()
expect(replacement.guest.close).toHaveBeenCalledOnce()
})
it('rejects registration without throwing when blank-document inspection fails', () => {
const { registry, routeSession } = createHarness()
const guest = createGuest({ session: routeSession })
registry.attachGuest(guest.guest)
vi.mocked(guest.guest.getURL).mockImplementation(() => {
throw new Error('guest unavailable')
})
expect(() => registry.registerGuest(page)).not.toThrow()
expect(registry.registerGuest(page)).toBe(false)
})
})
@@ -213,16 +213,6 @@ describe('LocalSshBrowserRoute', () => {
accepted.destroy()
})
it('retains one shared route per target', async () => {
const dependencies = {
resolveExecutionRoute: async () => fakeExecutionRoute('route-shared'),
getAuthority: () => ({ providerEpoch: 'epoch-1', connectionGeneration: 1 })
}
const first = await retainLocalSshBrowserRoute('target-shared', dependencies)
const second = await retainLocalSshBrowserRoute('target-shared', dependencies)
expect(second.port).toBe(first.port)
})
it('drops the shared route on explicit close so a re-add starts fresh', async () => {
const dependencies = {
resolveExecutionRoute: async () => fakeExecutionRoute('route-shared'),
@@ -236,60 +226,4 @@ describe('LocalSshBrowserRoute', () => {
socket.destroy()
expect(first.port).not.toBe(0)
})
it('classifies forwarding probes by the wire reason code, never by scary wording', async () => {
let failure: (Error & { reason?: number }) | null = null
const route = new LocalSshBrowserRoute('target-probe', {
resolveExecutionRoute: async () => {
const executionRoute = fakeExecutionRoute('route-probe')
const originalConnect = executionRoute.connect
executionRoute.connect = (target) => {
if (failure) {
const socket = new FakeTunnelSocket('never-connect')
const error = failure
queueMicrotask(() => {
socket.emit('error', error)
})
return socket
}
return originalConnect(target)
}
return executionRoute
},
getAuthority: () => ({ providerEpoch: 'epoch-1', connectionGeneration: 1 })
})
routes.push(route)
await route.listen()
const withReason = (message: string, reason?: number): Error & { reason?: number } => {
const error = new Error(message) as Error & { reason?: number }
if (reason !== undefined) {
error.reason = reason
}
return error
}
failure = withReason('(SSH) Channel open failure: open failed', 1)
expect(await route.probeForwarding(500)).toBe('forwarding-blocked')
// CONNECT_FAILED means forwarding was permitted even if the destination firewall says "denied".
failure = withReason('(SSH) Channel open failure: connection denied by firewall', 2)
expect(await route.probeForwarding(500)).toBe('ok')
failure = withReason('administratively prohibited: open failed')
expect(await route.probeForwarding(500)).toBe('forwarding-blocked')
failure = withReason('open forbidden: access denied, not allowed')
expect(await route.probeForwarding(500)).toBe('ok')
failure = null
expect(await route.probeForwarding(500)).toBe('ok')
})
it('reports ssh-unavailable when the probe cannot resolve a route', async () => {
const route = new LocalSshBrowserRoute('target-down', {
resolveExecutionRoute: async () => {
throw new Error('browser_tunnel_execution_host_unavailable')
},
getAuthority: () => ({ providerEpoch: 'epoch-1', connectionGeneration: 1 })
})
routes.push(route)
expect(await route.probeForwarding(500)).toBe('ssh-unavailable')
})
})
@@ -1,4 +1,4 @@
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
import { mkdirSync, mkdtempSync, readFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
@@ -52,69 +52,6 @@ describe('BrowserRoutePartitionBindingStore', () => {
})
})
it('reads pre-scope v1 metadata as unscoped bindings', () => {
const filePath = createPath()
writeFileSync(filePath, JSON.stringify({ version: 1, bindings: { [partition]: fingerprint } }))
const store = new BrowserRoutePartitionBindingStore({ filePath })
expect(store.get(partition)).toBe(fingerprint)
expect(store.listBindings().get(partition)).toEqual({
fingerprint,
storageScope: null,
lastUsedAt: 0
})
})
it('removes bindings and rejects a non-hex storage scope', () => {
const filePath = createPath()
const store = new BrowserRoutePartitionBindingStore({ filePath })
store.set(partition, fingerprint, 'e'.repeat(64))
expect(() => store.set(thirdPartition, fingerprint, 'z')).toThrow(
'browser_route_partition_binding_invalid'
)
expect(store.remove([partition])).toBe(1)
expect(store.remove([partition])).toBe(0)
expect(new BrowserRoutePartitionBindingStore({ filePath }).get(partition)).toBeNull()
})
it('rejects replacement and invalid binding shapes', () => {
const store = new BrowserRoutePartitionBindingStore({ filePath: createPath() })
store.set(partition, fingerprint, 'e'.repeat(64))
expect(() => store.set(partition, 'b'.repeat(64), 'e'.repeat(64))).toThrow(
'browser_route_partition_binding_conflict'
)
expect(() => store.set('persist:unowned', fingerprint, 'e'.repeat(64))).toThrow(
'browser_route_partition_binding_invalid'
)
expect(() => store.set(partition, 'short', 'e'.repeat(64))).toThrow(
'browser_route_partition_binding_invalid'
)
})
it('fails closed on corrupt or malformed persisted metadata', () => {
const filePath = createPath()
writeFileSync(filePath, '{broken')
expect(() => new BrowserRoutePartitionBindingStore({ filePath }).get(partition)).toThrow(
'browser_route_partition_binding_store_invalid'
)
writeFileSync(filePath, JSON.stringify({ version: 3, bindings: {} }))
expect(() => new BrowserRoutePartitionBindingStore({ filePath }).get(partition)).toThrow(
'browser_route_partition_binding_store_invalid'
)
})
it('rejects an oversized binding file before parsing it', () => {
const filePath = createPath()
writeFileSync(filePath, JSON.stringify({ version: 1, bindings: {}, padding: 'x'.repeat(256) }))
expect(() =>
new BrowserRoutePartitionBindingStore({ filePath, maxFileBytes: 128 }).get(partition)
).toThrow('browser_route_partition_binding_store_invalid')
})
it('fails closed when Chromium data exists without matching binding metadata', () => {
const paths = createStorePaths()
mkdirSync(join(paths.partitionDataRoot, partition.slice('persist:'.length)), {
@@ -128,46 +65,6 @@ describe('BrowserRoutePartitionBindingStore', () => {
)
})
it('accepts existing Chromium data only with matching durable metadata', () => {
const paths = createStorePaths()
const store = new BrowserRoutePartitionBindingStore(paths)
store.set(partition, fingerprint, 'e'.repeat(64))
mkdirSync(join(paths.partitionDataRoot, partition.slice('persist:'.length)), {
recursive: true
})
expect(store.get(partition)).toBe(fingerprint)
})
it('bounds distinct persisted bindings when liveness is unknown', () => {
const store = new BrowserRoutePartitionBindingStore({ filePath: createPath(), maxBindings: 1 })
store.set(partition, fingerprint, 'e'.repeat(64))
expect(() => store.set(otherPartition, 'b'.repeat(64), 'e'.repeat(64))).toThrow(
'browser_route_partition_binding_capacity'
)
})
it('evicts the least recently used releasable binding instead of failing at capacity', () => {
const store = new BrowserRoutePartitionBindingStore({
filePath: createPath(),
maxBindings: 2,
isPartitionRetained: () => false
})
vi.setSystemTime(1_000)
store.set(partition, fingerprint, 'e'.repeat(64))
vi.setSystemTime(2_000)
store.set(otherPartition, 'b'.repeat(64), 'e'.repeat(64))
vi.setSystemTime(3_000)
const evicted = store.set(thirdPartition, 'c'.repeat(64), 'e'.repeat(64))
expect(evicted).toEqual([partition])
expect(store.get(partition)).toBeNull()
expect(store.get(otherPartition)).toBe('b'.repeat(64))
expect(store.get(thirdPartition)).toBe('c'.repeat(64))
})
it('never evicts a retained partition, and fails only when every binding is retained', () => {
const filePath = createPath()
const retained = new Set<string>()
@@ -212,22 +109,6 @@ describe('BrowserRoutePartitionBindingStore', () => {
expect(store.get(partition)).toBe(fingerprint)
})
it('resolves an existing partition by fingerprint and re-points it on rebind', () => {
const store = new BrowserRoutePartitionBindingStore({ filePath: createPath() })
store.set(partition, fingerprint, 'e'.repeat(64))
expect(store.findPartitionByFingerprint(fingerprint)).toBe(partition)
expect(store.findPartitionByFingerprint('b'.repeat(64))).toBeNull()
store.rebind(partition, 'b'.repeat(64), 'e'.repeat(64))
expect(store.findPartitionByFingerprint('b'.repeat(64))).toBe(partition)
expect(store.get(partition)).toBe('b'.repeat(64))
expect(() => store.rebind(otherPartition, 'c'.repeat(64), 'e'.repeat(64))).toThrow(
'browser_route_partition_binding_invalid'
)
})
it('answers to an alias beside the fingerprint older builds read', () => {
const store = new BrowserRoutePartitionBindingStore({ filePath: createPath() })
store.set(partition, fingerprint, 'e'.repeat(64))
@@ -242,33 +123,4 @@ describe('BrowserRoutePartitionBindingStore', () => {
store.set(partition, fingerprint, 'f'.repeat(64))
expect(store.findPartitionByFingerprint('b'.repeat(64))).toBe(partition)
})
it('keeps aliases bounded and drops malformed ones without rejecting the file', () => {
const filePath = createPath()
writeFileSync(
filePath,
JSON.stringify({
version: 2,
bindings: {
[partition]: {
fingerprint,
storageScope: 'e'.repeat(64),
lastUsedAt: 1,
aliasFingerprints: ['not-a-fingerprint', 7, 'b'.repeat(64)]
}
}
})
)
const store = new BrowserRoutePartitionBindingStore({ filePath })
expect(store.listBindings().get(partition)?.aliasFingerprints).toEqual(['b'.repeat(64)])
store.rebind(partition, fingerprint, 'e'.repeat(64), 'c'.repeat(64))
store.rebind(partition, fingerprint, 'e'.repeat(64), 'd'.repeat(64))
expect(store.listBindings().get(partition)?.aliasFingerprints).toEqual([
'c'.repeat(64),
'd'.repeat(64)
])
expect(store.findPartitionByFingerprint('b'.repeat(64))).toBeNull()
})
})
@@ -136,45 +136,6 @@ function deferred(): {
}
describe('BrowserRouteSessionRegistry', () => {
it('prepares authority-scoped remote metadata without needing a desktop profile record', async () => {
const { dependencies, registry } = createHarness()
const profileMetadata = {
id: identity.browserProfileId,
label: 'Remote profile',
scope: 'isolated'
}
await prepare(registry, { profileMetadata })
expect(dependencies.validateProfile).toHaveBeenCalledWith(
identity.browserProfileId,
profileMetadata
)
expect(dependencies.setupPolicies).toHaveBeenCalledWith(
expect.objectContaining({
profileMetadata,
browserProfileId: identity.browserProfileId,
partition: 'persist:route-a'
})
)
})
it.each([
{ id: 'other-profile', label: 'Remote', scope: 'isolated' },
{
id: identity.browserProfileId,
label: 'Remote',
scope: 'isolated',
partition: 'persist:local-default'
}
])(
'rejects inconsistent or arbitrary partition metadata before touching storage',
async (profileMetadata) => {
const { registry, dependencies } = createHarness()
await expect(prepare(registry, { profileMetadata })).rejects.toThrow()
expect(dependencies.getSession).not.toHaveBeenCalled()
expect(dependencies.bindingStore.set).not.toHaveBeenCalled()
}
)
it('rekeys one prepared authority and makes the old release handle inert', async () => {
const { dependencies, registry } = createHarness()
const previousHandle = await prepare(registry)
@@ -204,126 +165,6 @@ describe('BrowserRouteSessionRegistry', () => {
expect(dependencies.clearPolicies).toHaveBeenCalledOnce()
})
it('applies and verifies the exact fail-closed proxy before allowlisting', async () => {
const { dependencies, order, registry, session } = createHarness()
const handle = await prepare(registry)
expect(handle.partition).toBe('persist:route-a')
expect(order).toEqual([
'validate-profile',
'persist-binding',
'get-session',
'set-proxy',
'setup-policies',
'close-connections',
'resolve-proxy'
])
expect(session.setProxy).toHaveBeenCalledWith({
mode: 'fixed_servers',
proxyRules: 'socks5://127.0.0.1:43123',
proxyBypassRules: '<-loopback>'
})
expect(session.resolveProxy).toHaveBeenCalledWith('http://browser-route-probe.invalid/')
expect(registry.isAllowedPartition(handle.partition)).toBe(true)
handle.release()
expect(registry.isAllowedPartition(handle.partition)).toBe(false)
expect(dependencies.clearPolicies).toHaveBeenCalledTimes(1)
})
it('indexes only exact live page generations by their Electron session', async () => {
const { registry, session } = createHarness()
const handle = await prepare(registry)
expect(registry.getPartitionForSession(session)).toBe(handle.partition)
expect(
registry.getPreparedPageAuthority({
partition: handle.partition,
browserPageId: 'page-a',
pageHostGeneration: 1,
rendererWebContentsId: 11
})
).not.toBeNull()
expect(
registry.getPreparedPageAuthority({
partition: handle.partition,
browserPageId: 'page-a',
pageHostGeneration: 2,
rendererWebContentsId: 11
})
).toBeNull()
handle.release()
expect(registry.getPartitionForSession(session)).toBe(handle.partition)
expect(
registry.getPreparedPageAuthority({
partition: handle.partition,
browserPageId: 'page-a',
pageHostGeneration: 1,
rendererWebContentsId: 11
})
).toBeNull()
})
it('binds prepared page authority to the exact owning renderer', async () => {
const { registry } = createHarness()
const handle = await prepare(registry)
const page = {
partition: handle.partition,
browserPageId: 'page-a',
pageHostGeneration: 1
}
expect(registry.getPreparedPageAuthority({ ...page, rendererWebContentsId: 11 })).not.toBeNull()
expect(registry.getPreparedPageAuthority({ ...page, rendererWebContentsId: 12 })).toBeNull()
})
it('does not move a live page generation to another renderer', async () => {
const { registry } = createHarness()
const owned = await prepare(registry)
await expect(prepare(registry, { rendererWebContentsId: 12 })).rejects.toThrow(
'browser_route_partition_page_owner_conflict'
)
expect(
registry.getPreparedPageAuthority({
partition: owned.partition,
browserPageId: 'page-a',
pageHostGeneration: 1,
rendererWebContentsId: 11
})
).not.toBeNull()
})
it('retires prepared but unregistered pages when their owning renderer exits', async () => {
const { registry } = createHarness()
const owned = await prepare(registry)
await prepare(registry, {
browserPageId: 'page-b',
pageHostGeneration: 2,
rendererWebContentsId: 12
})
expect(registry.retirePreparedPagesOwnedByRenderer(11)).toBe(1)
expect(
registry.getPreparedPageAuthority({
partition: owned.partition,
browserPageId: 'page-a',
pageHostGeneration: 1,
rendererWebContentsId: 11
})
).toBeNull()
expect(
registry.getPreparedPageAuthority({
partition: owned.partition,
browserPageId: 'page-b',
pageHostGeneration: 2,
rendererWebContentsId: 12
})
).not.toBeNull()
})
it('continues owner retirement across partitions when policy cleanup throws', async () => {
const { registry } = createHarness({ cleanupError: new Error('cleanup unavailable') })
const first = await prepare(registry)
@@ -342,23 +183,6 @@ describe('BrowserRouteSessionRegistry', () => {
}
})
it('rejects an in-flight prepare after its renderer owner exits', async () => {
const probeStarted = deferred()
const resolveProxyGate = deferred()
const { dependencies, registry } = createHarness({
proxyProbeStarted: probeStarted.resolve,
resolveProxyGate: resolveProxyGate.promise
})
const preparing = prepare(registry)
await probeStarted.promise
expect(registry.retirePreparedPagesOwnedByRenderer(11)).toBe(0)
resolveProxyGate.resolve()
await expect(preparing).rejects.toThrow('browser_route_partition_renderer_retired')
expect(registry.isAllowedPartition('persist:route-a')).toBe(false)
expect(dependencies.clearPolicies).toHaveBeenCalledOnce()
})
it('admits a live sibling waiter after fencing a stale pending owner', async () => {
const probeStarted = deferred()
const resolveProxyGate = deferred()
@@ -389,25 +213,6 @@ describe('BrowserRouteSessionRegistry', () => {
expect(dependencies.clearPolicies).not.toHaveBeenCalled()
})
it('bounds callers waiting on one pending partition setup', async () => {
const probeStarted = deferred()
const resolveProxyGate = deferred()
const { registry } = createHarness({
maxPagesPerPartition: 1,
proxyProbeStarted: probeStarted.resolve,
resolveProxyGate: resolveProxyGate.promise
})
const first = prepare(registry)
await probeStarted.promise
await expect(prepare(registry, { browserPageId: 'page-b' })).rejects.toThrow(
'browser_route_partition_pending_capacity'
)
resolveProxyGate.resolve()
const handle = await first
handle.release()
})
it('does not resolve a live-partition prepare after owner retirement', async () => {
const { registry } = createHarness()
const first = await prepare(registry)
@@ -422,31 +227,6 @@ describe('BrowserRouteSessionRegistry', () => {
expect(registry.isAllowedPartition(first.partition)).toBe(false)
})
it('changes opaque page authority when the same logical tuple is prepared again', async () => {
const { registry } = createHarness()
const first = await prepare(registry)
const firstAuthority = registry.getPreparedPageAuthority({
partition: first.partition,
browserPageId: 'page-a',
pageHostGeneration: 1,
rendererWebContentsId: 11
})
first.release()
const replacement = await prepare(registry)
const replacementAuthority = registry.getPreparedPageAuthority({
partition: replacement.partition,
browserPageId: 'page-a',
pageHostGeneration: 1,
rendererWebContentsId: 11
})
expect(firstAuthority).not.toBeNull()
expect(replacementAuthority).not.toBeNull()
expect(replacementAuthority).not.toBe(firstAuthority)
replacement.release()
})
it('keeps route policy installed until delayed exact-page retirement settles', async () => {
const { dependencies, registry, retirements } = createHarness({
maxPagesPerPartition: 1,
@@ -493,72 +273,6 @@ describe('BrowserRouteSessionRegistry', () => {
expect(dependencies.clearPolicies).toHaveBeenCalledOnce()
})
it('fails closed when exact-page retirement cannot be started', async () => {
const { dependencies, registry } = createHarness({
retirementError: new Error('retirement unavailable')
})
const handle = await prepare(registry)
expect(() => handle.release()).not.toThrow()
expect(registry.isAllowedPartition(handle.partition)).toBe(false)
expect(dependencies.clearPolicies).not.toHaveBeenCalled()
await expect(prepare(registry)).rejects.toThrow('browser_route_partition_page_retiring')
})
it('lets an exact lifecycle event begin the same fenced page retirement', async () => {
const { registry, retirements } = createHarness({ retirementSettled: false })
const handle = await prepare(registry)
const pageAuthority = registry.getPreparedPageAuthority({
partition: handle.partition,
browserPageId: 'page-a',
pageHostGeneration: 1,
rendererWebContentsId: 11
})
expect(
registry.retirePreparedPage({
partition: handle.partition,
browserPageId: 'page-a',
pageHostGeneration: 1,
rendererWebContentsId: 11,
pageAuthority: pageAuthority ?? Symbol('missing')
})
).toBe(true)
expect(registry.isAllowedPartition(handle.partition)).toBe(false)
expect(retirements).toHaveLength(1)
expect(
registry.retirePreparedPage({
partition: handle.partition,
browserPageId: 'page-a',
pageHostGeneration: 1,
rendererWebContentsId: 11,
pageAuthority: pageAuthority ?? Symbol('missing')
})
).toBe(false)
})
it('never allowlists a partition whose proxy resolves direct or elsewhere', async () => {
const { dependencies, registry } = createHarness({ resolvedProxy: 'DIRECT' })
await expect(prepare(registry)).rejects.toThrow(
'browser_route_partition_proxy_verification_failed'
)
expect(registry.isAllowedPartition('persist:route-a')).toBe(false)
expect(dependencies.clearPolicies).toHaveBeenCalledTimes(1)
})
it('clears partially installed policies when async policy setup fails', async () => {
const { dependencies, registry, session } = createHarness({
setupError: new Error('policy setup failed')
})
await expect(prepare(registry)).rejects.toThrow('policy setup failed')
expect(session.setProxy).toHaveBeenCalledTimes(1)
expect(session.closeAllConnections).toHaveBeenCalledTimes(1)
expect(dependencies.clearPolicies).toHaveBeenCalledTimes(1)
expect(registry.isAllowedPartition('persist:route-a')).toBe(false)
})
it.each([false, true])(
'keeps retries joined until failed policy setup settles proxy cleanup (reject: %s)',
async (rejectProxy) => {
@@ -597,43 +311,6 @@ describe('BrowserRouteSessionRegistry', () => {
}
)
it('rejects a binding collision before opening an Electron session', async () => {
const { bindings, dependencies, registry } = createHarness()
bindings.set('persist:route-a', 'c'.repeat(64))
await expect(prepare(registry)).rejects.toThrow('browser_route_partition_binding_conflict')
expect(dependencies.getSession).not.toHaveBeenCalled()
})
it('rejects a missing browser profile before consuming durable binding capacity', async () => {
const { dependencies, registry } = createHarness({
profileError: new Error('browser_route_partition_profile_unavailable')
})
await expect(prepare(registry)).rejects.toThrow('browser_route_partition_profile_unavailable')
expect(dependencies.bindingStore.set).not.toHaveBeenCalled()
expect(dependencies.getSession).not.toHaveBeenCalled()
})
it('rejects invalid listener and page identities before binding persistence', async () => {
const { dependencies, registry } = createHarness()
await expect(
prepare(registry, { proxyEndpoint: { host: '0.0.0.0', port: 43123 } })
).rejects.toThrow('browser_route_partition_proxy_invalid')
await expect(prepare(registry, { browserPageId: '' })).rejects.toThrow(
'browser_route_partition_page_invalid'
)
await expect(prepare(registry, { pageHostGeneration: 0 })).rejects.toThrow(
'browser_route_partition_page_invalid'
)
await expect(prepare(registry, { rendererWebContentsId: 0 })).rejects.toThrow(
'browser_route_partition_page_invalid'
)
expect(dependencies.bindingStore.set).not.toHaveBeenCalled()
expect(dependencies.getSession).not.toHaveBeenCalled()
})
it('shares one prepared partition and fences stale page-handle cleanup', async () => {
const { dependencies, registry, session } = createHarness()
const first = await prepare(registry)
@@ -664,27 +341,4 @@ describe('BrowserRouteSessionRegistry', () => {
first.release()
second.release()
})
it('bounds distinct live and pending partitions', async () => {
const { registry } = createHarness({ maxLivePartitions: 1 })
const first = await prepare(registry)
await expect(
prepare(registry, {
browserPageId: 'page-b',
identity: { ...identity, executionHostIdentity: 'execution-host-b' }
})
).rejects.toThrow('browser_route_partition_capacity')
first.release()
})
it('bounds distinct page generations retained by one partition', async () => {
const { registry } = createHarness({ maxPagesPerPartition: 1 })
const first = await prepare(registry)
await expect(prepare(registry, { browserPageId: 'page-b' })).rejects.toThrow(
'browser_route_partition_page_capacity'
)
first.release()
})
})
@@ -137,66 +137,12 @@ function profileFor(partition: string): BrowserSessionProfile {
}
}
function fireWillDownload(partition: string): { cancelled: boolean } {
const sess = sessionsByPartition.get(partition)
if (!sess || sess.listeners.length !== 1) {
throw new Error(`expected exactly one will-download listener on ${partition}`)
}
let cancelled = false
sess.listeners[0]({ preventDefault: () => (cancelled = true) }, { id: 'item-1' }, { id: 42 })
return { cancelled }
}
beforeEach(() => {
vi.clearAllMocks()
sessionsByPartition.clear()
vi.resetModules()
})
describe('partition download policy', () => {
it('routes a download on a partition that did not ask for the deny', async () => {
const install = await loadInstaller()
install(profileFor('persist:browsing-1'))
expect(fireWillDownload('persist:browsing-1').cancelled).toBe(false)
expect(mocks.handleGuestWillDownload).toHaveBeenCalledWith(
expect.objectContaining({ guestWebContentsId: 42 })
)
})
it('cancels a download on a partition that asked for the deny, routing nothing', async () => {
const install = await loadInstaller()
install(profileFor('orca-doc-preview'), { downloads: 'deny' })
expect(fireWillDownload('orca-doc-preview').cancelled).toBe(true)
expect(mocks.handleGuestWillDownload).not.toHaveBeenCalled()
})
it('tells the reader about the refusal, and only on the partition that refused', async () => {
const install = await loadInstaller()
install(profileFor('orca-doc-preview'), { downloads: 'deny' })
install(profileFor('persist:browsing-1'))
fireWillDownload('persist:browsing-1')
expect(mocks.noticeDocPreviewDownloadBlocked).not.toHaveBeenCalled()
fireWillDownload('orca-doc-preview')
expect(mocks.noticeDocPreviewDownloadBlocked).toHaveBeenCalledWith(
expect.objectContaining({ id: 42 })
)
})
it('keeps each partition on its own decision', async () => {
const install = await loadInstaller()
install(profileFor('orca-doc-preview'), { downloads: 'deny' })
install(profileFor('persist:browsing-1'))
expect(fireWillDownload('orca-doc-preview').cancelled).toBe(true)
expect(fireWillDownload('persist:browsing-1').cancelled).toBe(false)
expect(mocks.handleGuestWillDownload).toHaveBeenCalledTimes(1)
})
})
describe('partition permission policy', () => {
it('denies clipboard reads only to pages an SSH host has driven (#29145)', async () => {
mocks.isRemoteCliDrivenGuest.mockImplementation((id?: number) => id === 7)
@@ -245,44 +191,6 @@ describe('partition permission policy', () => {
expect(checkHandler(guest(8), 'clipboard-read', 'https://a.test')).toBe(true)
})
it('answers non-notification requests without inventorying frames', async () => {
const install = await loadInstaller()
const partition = 'persist:permission-frame-inventory'
await install(profileFor(partition), { sitePermissions: 'session' })
const { sessionBrowserSitePermissions } =
await import('../site-settings/browser-site-permission-prompt')
const memory = sessionBrowserSitePermissions(partition)
const requestHandler =
sessionsByPartition.get(partition)?.setPermissionRequestHandler.mock.calls[0]?.[0]
if (typeof requestHandler !== 'function') {
throw new Error('Expected the installed permission request handler')
}
const enumerateFrames = vi.fn(() => [{ url: 'about:srcdoc', origin: 'https://other.test' }])
const tab = {
id: 12,
getURL: () => 'https://a.test/page',
mainFrame: {
get framesInSubtree() {
return enumerateFrames()
}
}
}
for (const decision of ['allow', 'block'] as const) {
memory.set('https://a.test', ['location', 'clipboard', 'midi'], decision)
for (const permission of ['geolocation', 'clipboard-read', 'midi']) {
const answer = await new Promise<boolean>((resolve) => {
requestHandler(tab, permission, resolve, {
requestingUrl: 'about:srcdoc',
isMainFrame: false
})
})
expect(answer).toBe(decision === 'allow')
}
}
expect(enumerateFrames).not.toHaveBeenCalled()
})
it("answers a notification from a frame with an inherited origin as the frame's real site", async () => {
const install = await loadInstaller()
const store = await import('../site-settings/browser-site-permission-store')
@@ -499,119 +407,4 @@ describe('partition permission policy', () => {
expect.objectContaining({ guestWebContentsId: 11, permission: 'fileSystem' })
)
})
it('refuses a blocklisted system folder at once instead of leaving the page waiting', async () => {
const install = await loadInstaller()
install(profileFor('persist:browsing-6'))
install(profileFor('orca-doc-preview'), { permissions: 'deny' })
const answer = (partition: string) => {
const sess = sessionsByPartition.get(partition)
const listener = sess?.on.mock.calls.find(
([event]) => event === 'file-system-access-restricted'
)?.[1]
const actions: string[] = []
listener?.(
{},
{
origin: 'https://app.test',
isDirectory: true,
path: '/Users/me',
webContents: { id: 12 }
},
(action: string) => actions.push(action)
)
return actions
}
expect(answer('orca-doc-preview')).toEqual(['deny'])
expect(mocks.notifyPermissionDenied).not.toHaveBeenCalled()
expect(answer('persist:browsing-6')).toEqual(['deny'])
expect(mocks.notifyPermissionDenied).toHaveBeenCalledWith({
guestWebContentsId: 12,
permission: 'fileSystem',
rawUrl: 'https://app.test'
})
})
it('hands a link to another app to the open-app prompt instead of denying it', async () => {
const install = await loadInstaller()
install(profileFor('persist:browsing-4'))
const sess = sessionsByPartition.get('persist:browsing-4')
if (!sess) {
throw new Error('Expected the browsing session')
}
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the installer registered exactly this handler on the fake session.
const requestHandler = sess.setPermissionRequestHandler.mock.calls[0]?.[0] as (
webContents: { id: number; getURL: () => string },
permission: string,
callback: (allowed: boolean) => void,
details: { externalURL?: string; requestingUrl?: string }
) => void
const guest = { id: 9, getURL: () => 'https://acme.okta.com/signin' }
const details = {
externalURL: 'com-okta-authenticator:/deviceChallenge',
requestingUrl: 'https://login.acme.okta.com/frame'
}
const decisions: boolean[] = []
mocks.requestExternalAppLaunch.mockReturnValueOnce(true)
requestHandler(guest, 'openExternal', (allowed) => decisions.push(allowed), details)
const [launchGuest, launchUrl, requestingUrl] = mocks.requestExternalAppLaunch.mock.calls[0]
expect([launchGuest, launchUrl]).toEqual([guest, 'com-okta-authenticator:/deviceChallenge'])
expect(requestingUrl()).toBe('https://login.acme.okta.com/frame')
expect(mocks.notifyPermissionDenied).not.toHaveBeenCalled()
mocks.requestExternalAppLaunch.mockReturnValueOnce(false)
requestHandler(guest, 'openExternal', (allowed) => decisions.push(allowed), details)
expect(mocks.notifyPermissionDenied).toHaveBeenCalledWith(
expect.objectContaining({ guestWebContentsId: 9, permission: 'openExternal' })
)
// Orca launches approved apps itself, so Chromium's launch remains denied.
expect(decisions).toEqual([false, false])
})
it('cancels a download from a page an SSH host drives (#29145)', async () => {
mocks.isRemoteCliDrivenGuest.mockImplementation((id?: number) => id === 42)
const install = await loadInstaller()
install(profileFor('persist:browsing-3'))
expect(fireWillDownload('persist:browsing-3')).toEqual({ cancelled: true })
expect(mocks.handleGuestWillDownload).not.toHaveBeenCalled()
})
it('denies every request and check on a strict partition without WebAuthn handlers', async () => {
const install = await loadInstaller()
install(profileFor('orca-doc-preview'), { permissions: 'deny' })
const sess = sessionsByPartition.get('orca-doc-preview')
if (!sess) {
throw new Error('Expected the preview session')
}
const requestHandler = sess.setPermissionRequestHandler.mock.calls[0]?.[0] as (
webContents: Electron.WebContents,
permission: string,
callback: (allowed: boolean) => void
) => void
const checkHandler = sess.setPermissionCheckHandler.mock.calls[0]?.[0] as (
webContents: Electron.WebContents,
permission: string
) => boolean
const displayMediaHandler = sess.setDisplayMediaRequestHandler.mock.calls[0]?.[0] as (
request: Electron.DisplayMediaRequestHandlerHandlerRequest,
callback: (streams: { video?: Electron.WebFrameMain; audio?: 'loopback' }) => void
) => void
for (const permission of ['media', 'clipboard-read', 'notifications', 'fullscreen']) {
let decision: boolean | null = null
requestHandler({} as Electron.WebContents, permission, (allowed) => (decision = allowed))
expect(decision).toBe(false)
expect(checkHandler({} as Electron.WebContents, permission)).toBe(false)
}
expect(mocks.installBrowserWebAuthnAccessHandlers).not.toHaveBeenCalled()
expect(mocks.clearBrowserWebAuthnAccessHandlers).toHaveBeenCalledWith(sess)
let displayMediaDecision: { video?: Electron.WebFrameMain; audio?: 'loopback' } | null = null
displayMediaHandler({} as Electron.DisplayMediaRequestHandlerHandlerRequest, (decision) => {
displayMediaDecision = decision
})
expect(displayMediaDecision).toEqual({ video: undefined, audio: undefined })
})
})
@@ -33,7 +33,6 @@ import {
invalidateBrowserSessionProxyApplication,
setBrowserNetworkProxySettingsResolver
} from './browser-session-proxy'
import { handleElectronProxyLogin } from '../../network/electron-proxy-credentials'
import {
applyProxySettingsToSession,
releaseProxySessionApplication
@@ -68,110 +67,6 @@ describe('browser session proxy', () => {
vi.restoreAllMocks()
})
it('pins the configured proxy onto every browser partition', async () => {
await applyBrowserSessionProxies(PROFILES, {
httpProxyUrl: 'socks5://127.0.0.1:1080',
httpProxyBypassRules: ''
})
expect(fromPartitionMock).toHaveBeenCalledWith('persist:orca-browser')
expect(fromPartitionMock).toHaveBeenCalledWith('persist:orca-browser-session-iso')
for (const partition of PROFILES.map((p) => p.partition)) {
expect(sessionsByPartition.get(partition)?.setProxy).toHaveBeenCalledWith({
mode: 'fixed_servers',
proxyRules: 'socks5://127.0.0.1:1080'
})
}
})
it('carries bypass rules through to each partition', async () => {
await applyBrowserSessionProxies(PROFILES, {
httpProxyUrl: 'socks5://127.0.0.1:1080',
httpProxyBypassRules: 'localhost, *.internal'
})
expect(sessionsByPartition.get('persist:orca-browser')?.setProxy).toHaveBeenCalledWith({
mode: 'fixed_servers',
proxyRules: 'socks5://127.0.0.1:1080',
proxyBypassRules: 'localhost;*.internal'
})
})
it('authenticates a credentialed proxy without exposing credentials in partition rules', async () => {
await applyBrowserSessionProxies(PROFILES.slice(0, 1), {
httpProxyUrl: 'http://browser-user:browser-pass@proxy.example:8080',
httpProxyBypassRules: ''
})
const sess = sessionsByPartition.get('persist:orca-browser')
expect(sess?.setProxy).toHaveBeenCalledWith({
mode: 'fixed_servers',
proxyRules: 'http://proxy.example:8080'
})
const preventDefault = vi.fn()
const callback = vi.fn()
handleElectronProxyLogin(
{ preventDefault } as never,
{ session: sess } as never,
{} as never,
{
isProxy: true,
scheme: 'basic',
host: 'proxy.example',
port: 8080,
realm: 'proxy'
},
callback
)
expect(preventDefault).toHaveBeenCalledOnce()
expect(callback).toHaveBeenCalledWith('browser-user', 'browser-pass')
})
it('keeps sweeping but reports failure so the failed partition stays gated', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
fromPartitionMock.mockImplementationOnce(() => {
throw new Error('partition unavailable')
})
await expect(
applyBrowserSessionProxies(PROFILES, {
httpProxyUrl: 'socks5://127.0.0.1:1080',
httpProxyBypassRules: ''
})
).rejects.toThrow('persist:orca-browser')
expect(
sessionsByPartition.get('persist:orca-browser-session-iso')?.setProxy
).toHaveBeenCalledWith({ mode: 'fixed_servers', proxyRules: 'socks5://127.0.0.1:1080' })
expect(warn).toHaveBeenCalledWith(
'[proxy] Failed to apply proxy to browser partition',
'persist:orca-browser'
)
})
it('starts every partition write without waiting for earlier partitions', async () => {
let finishFirstWrite: (() => void) | undefined
const firstSession = fromPartition('persist:orca-browser')
firstSession.setProxy.mockImplementation(
() => new Promise<void>((resolve) => (finishFirstWrite = resolve))
)
sessionsByPartition.set('persist:orca-browser', firstSession)
const sweep = applyBrowserSessionProxies(PROFILES, {
httpProxyUrl: 'socks5://127.0.0.1:1080',
httpProxyBypassRules: ''
})
await vi.waitFor(() =>
expect(
sessionsByPartition.get('persist:orca-browser-session-iso')?.setProxy
).toHaveBeenCalledOnce()
)
finishFirstWrite?.()
await sweep
})
it('settles an in-flight new partition on the latest swept policy', async () => {
let proxyUrl = 'http://old.example:8080'
let finishFirstWrite: (() => void) | undefined
@@ -218,29 +113,6 @@ describe('browser session proxy', () => {
])
})
it('reads settings through the injected resolver when none are passed', async () => {
setBrowserNetworkProxySettingsResolver(() => ({
httpProxyUrl: 'socks5://127.0.0.1:1080',
httpProxyBypassRules: ''
}))
const sess = fromPartitionMock('persist:orca-browser')
await applyProxyToBrowserSession(sess as never)
expect(sess.setProxy).toHaveBeenCalledWith({
mode: 'fixed_servers',
proxyRules: 'socks5://127.0.0.1:1080'
})
})
it('makes no proxy write when no resolver is registered', async () => {
const sess = fromPartitionMock('persist:orca-browser')
await applyProxyToBrowserSession(sess as never)
expect(sess.setProxy).not.toHaveBeenCalled()
})
describe('startup readiness', () => {
beforeEach(() => {
for (const key of [
@@ -292,24 +164,6 @@ describe('browser session proxy', () => {
}
})
it('retries only a failed installation after its bounded attempts are exhausted', async () => {
const failed = fromPartition('persist:orca-browser')
const healthy = fromPartition('persist:orca-browser-session-iso')
for (let attempt = 0; attempt < 3; attempt++) {
failed.resolveProxy.mockRejectedValueOnce(new Error('native proxy failure'))
}
const initialFailure = expect(applyProxySettingsToSession(failed, {})).rejects.toThrow(
'native proxy failure'
)
await applyProxySettingsToSession(healthy, {})
await initialFailure
await awaitBrowserSessionProxies(PROFILES)
expect(failed.resolveProxy).toHaveBeenCalledTimes(4)
expect(healthy.resolveProxy).toHaveBeenCalledOnce()
})
it('awaits every installation and preserves failed-partition warnings and retry budget', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
const failed = fromPartition('persist:orca-browser')
@@ -1,10 +1,8 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
CLEAN_USER_AGENT,
createFsState,
IDENTITY_RECORD_PATH,
installModuleMocks,
META_PATH,
seedMeta
} from './browser-session-registry-persistence-test-fixture'
@@ -14,84 +12,6 @@ describe('BrowserSessionRegistry retired identity data', () => {
vi.restoreAllMocks()
})
it('ignores legacy persisted UAs, valid or broken, and applies the engine UA', async () => {
const importedPartition = 'persist:orca-browser-session-11111111-1111-4111-8111-111111111111'
const brokenUa =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/1.158.1 Safari/537.36'
const validUa = 'Mozilla/5.0 Chrome/120.0.0.0 Safari/537.36'
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: { browserFamily: 'arc', importedAt: 1 },
userAgent: brokenUa,
userAgentByPartition: {
'persist:orca-browser': brokenUa,
[importedPartition]: validUa
},
pendingCookieDbPath: null,
pendingCookieImports: {},
profiles: [
{
id: '11111111-1111-4111-8111-111111111111',
scope: 'imported',
partition: importedPartition,
label: 'Imported',
source: { browserFamily: 'chrome', importedAt: 1 }
}
]
})
const { sessionFromPartitionMock, installBrowserSessionUserAgentPolicyMock } =
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.initializeBrowserSessionsFromPersistedState()
const appliedUas = sessionFromPartitionMock.mock.results.flatMap((r) =>
r.value.setUserAgent.mock.calls.map((c: unknown[]) => c[0])
)
expect(appliedUas).not.toContain(brokenUa)
expect(appliedUas).not.toContain(validUa)
expect(appliedUas.length).toBeGreaterThan(0)
expect(appliedUas.every((ua) => ua === CLEAN_USER_AGENT)).toBe(true)
expect(installBrowserSessionUserAgentPolicyMock).toHaveBeenCalled()
})
it('flags the retired per-profile choice without rewriting its persisted bytes', async () => {
const importedPartition = 'persist:orca-browser-session-11111111-1111-4111-8111-111111111111'
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
profiles: [
{
id: '11111111-1111-4111-8111-111111111111',
scope: 'imported',
partition: importedPartition,
label: 'Imported',
source: { browserFamily: 'comet', importedAt: 1 },
userAgentMode: 'native'
}
]
})
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.initializeBrowserSessionsFromPersistedState()
await vi.waitFor(() =>
expect(JSON.parse(fsState.files.get(IDENTITY_RECORD_PATH) ?? '{}')).toEqual({
version: 1,
mode: 'clean',
explicitSelection: false,
migrationNoticePending: true
})
)
// Preserve retired choices for rollback.
expect(JSON.parse(fsState.files.get(META_PATH) ?? '{}').profiles[0].userAgentMode).toBe(
'native'
)
})
it('does not re-arm the retired-choice notice on the launch after an explicit choice', async () => {
const profileId = '11111111-1111-4111-8111-111111111111'
const fsState = createFsState()
@@ -128,82 +48,4 @@ describe('BrowserSessionRegistry retired identity data', () => {
migrationNoticePending: false
})
})
it.each([
{ scenario: 'malformed members', malformed: [null, 42, 'broken'], failWrite: false },
{ scenario: 'a read-only notice', malformed: [], failWrite: true }
])('hydrates the valid profile despite $scenario', async ({ malformed, failWrite }) => {
const profileId = '11111111-1111-4111-8111-111111111111'
const partition = `persist:orca-browser-session-${profileId}`
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
profiles: [
...malformed,
{
id: profileId,
scope: 'isolated',
partition,
label: 'Existing',
source: null,
userAgentMode: 'native'
}
]
})
installModuleMocks(fsState, new Set(), failWrite)
const errors = vi.spyOn(console, 'error').mockImplementation(() => {})
const { browserSessionRegistry } = await import('./browser-session-registry')
expect(() => browserSessionRegistry.initializeBrowserSessionsFromPersistedState()).not.toThrow()
expect(browserSessionRegistry.getProfile(profileId)?.partition).toBe(partition)
if (failWrite) {
await vi.waitFor(() => expect(errors).toHaveBeenCalled())
expect(errors.mock.calls[0]?.[1]).toMatchObject({ message: 'read-only userData' })
}
await vi.waitFor(() => expect(fsState.files.has(IDENTITY_RECORD_PATH)).toBe(!failWrite))
const written = JSON.parse(fsState.files.get(META_PATH) ?? '{}')
expect(written.profiles).toHaveLength(malformed.length + 1)
expect(written.profiles.at(-1).userAgentMode).toBe('native')
})
it('hydrates a retired native profile under the process identity', async () => {
const importedPartition = 'persist:orca-browser-session-12121212-1212-4121-8121-121212121212'
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
userAgentByPartition: {},
pendingCookieDbPath: null,
pendingCookieImports: {},
profiles: [
{
id: '12121212-1212-4121-8121-121212121212',
scope: 'isolated',
partition: importedPartition,
label: 'Google',
source: null,
userAgentMode: 'native'
}
]
})
const { sessionFromPartitionMock, installBrowserSessionUserAgentPolicyMock } =
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.initializeBrowserSessionsFromPersistedState()
const importedSessions = sessionFromPartitionMock.mock.results
.filter((_, index) => sessionFromPartitionMock.mock.calls[index]?.[0] === importedPartition)
.map((result) => result.value)
expect(importedSessions.length).toBeGreaterThan(0)
expect(
importedSessions.every((sess) => sess.setUserAgent.mock.calls[0]?.[0] === CLEAN_USER_AGENT)
).toBe(true)
expect(
installBrowserSessionUserAgentPolicyMock.mock.calls.some(
([sess]) => sess.partition === importedPartition
)
).toBe(true)
})
})
@@ -1,518 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
CLEAN_USER_AGENT,
createFsState,
installModuleMocks,
META_PATH,
seedMeta,
seedSitePermissions
} from './browser-session-registry-persistence-test-fixture'
describe('BrowserSessionRegistry persistence', () => {
beforeEach(() => {
vi.resetModules()
vi.restoreAllMocks()
})
it('expires a legacy pendingCookieDbPath without copying it over the live jar', async () => {
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
pendingCookieDbPath: '/staged/legacy',
profiles: []
})
fsState.present.add('/staged/legacy')
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.applyPendingCookieImport()
const written = JSON.parse(fsState.files.get(META_PATH) ?? '{}')
expect(written.pendingCookieDbPath).toBeNull()
expect(written.pendingCookieImports).toEqual({})
expect(fsState.present.has('/user-data/Partitions/orca-browser/Cookies')).toBe(false)
})
it('keeps the existing Network database when expiring an unmarked legacy image', async () => {
const stagedPath = '/staged/network-import'
const networkPath = '/user-data/Partitions/orca-browser/Network/Cookies'
const legacyPath = '/user-data/Partitions/orca-browser/Cookies'
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
pendingCookieDbPath: stagedPath,
profiles: []
})
fsState.files.set(stagedPath, 'imported cookies')
fsState.files.set(networkPath, 'old cookies')
fsState.present.add(stagedPath)
fsState.present.add(networkPath)
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.applyPendingCookieImport()
expect(fsState.files.get(networkPath)).toBe('old cookies')
expect(fsState.present.has(legacyPath)).toBe(false)
})
it('persists new browser session profiles under the active Orca profile directory', async () => {
const fsState = createFsState()
const profileMetaPath = '/user-data/profiles/local-work/browser-session-meta.json'
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.configureForOrcaProfile({
orcaProfileId: 'local-work',
profileDirectory: '/user-data/profiles/local-work'
})
const profile = await browserSessionRegistry.createProfile('isolated', 'Work Browser')
expect(profile).not.toBeNull()
expect(fsState.files.has(profileMetaPath)).toBe(true)
expect(fsState.files.has(META_PATH)).toBe(false)
expect(JSON.parse(fsState.files.get(profileMetaPath) ?? '{}').profiles[0]).toMatchObject({
id: profile!.id,
partition: profile!.partition,
label: 'Work Browser'
})
})
it('applies the process identity and request exceptions to new profiles', async () => {
const fsState = createFsState()
const { sessionFromPartitionMock, installBrowserSessionUserAgentPolicyMock } =
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
await browserSessionRegistry.createProfile('isolated', 'Default identity')
const profileSession = sessionFromPartitionMock.mock.results.at(-1)?.value
expect(profileSession.setUserAgent).toHaveBeenCalledWith(CLEAN_USER_AGENT)
expect(installBrowserSessionUserAgentPolicyMock).toHaveBeenCalledWith(
profileSession,
expect.any(Function)
)
})
it('clears only the requested partition and unlinks its staged database files', async () => {
const otherPartition = 'persist:orca-browser-session-bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb'
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
userAgentByPartition: {},
pendingCookieDbPath: '/staged/default',
pendingCookieImports: {
'persist:orca-browser': '/staged/default',
[otherPartition]: '/staged/other'
},
profiles: []
})
for (const suffix of ['', '-wal', '-shm']) {
fsState.files.set(`/staged/other${suffix}`, 'db')
fsState.present.add(`/staged/other${suffix}`)
fsState.files.set(`/staged/default${suffix}`, 'db')
fsState.present.add(`/staged/default${suffix}`)
}
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.clearPendingCookieImport(otherPartition)
const written = JSON.parse(fsState.files.get(META_PATH) ?? '{}')
expect(written.pendingCookieImports).toEqual({ 'persist:orca-browser': '/staged/default' })
expect(written.pendingCookieDbPath).toBe('/staged/default')
for (const suffix of ['', '-wal', '-shm']) {
expect(fsState.present.has(`/staged/other${suffix}`)).toBe(false)
expect(fsState.present.has(`/staged/default${suffix}`)).toBe(true)
}
})
it('drops the legacy pointer when the default partition is the one cleared', async () => {
const otherPartition = 'persist:orca-browser-session-cccccccc-cccc-4ccc-8ccc-cccccccccccc'
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
userAgentByPartition: {},
pendingCookieDbPath: '/staged/default',
pendingCookieImports: {
'persist:orca-browser': '/staged/default',
[otherPartition]: '/staged/other'
},
profiles: []
})
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.clearPendingCookieImport('persist:orca-browser')
const written = JSON.parse(fsState.files.get(META_PATH) ?? '{}')
expect(written.pendingCookieImports).toEqual({ [otherPartition]: '/staged/other' })
expect(written.pendingCookieDbPath).toBeNull()
})
it('is a no-op when the partition has no pending import', async () => {
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
userAgentByPartition: {},
pendingCookieDbPath: '/staged/default',
pendingCookieImports: { 'persist:orca-browser': '/staged/default' },
profiles: []
})
fsState.files.set('/staged/default', 'db')
fsState.present.add('/staged/default')
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
const metaBefore = fsState.files.get(META_PATH)
browserSessionRegistry.clearPendingCookieImport('persist:orca-browser-session-unknown')
expect(fsState.files.get(META_PATH)).toBe(metaBefore)
expect(fsState.present.has('/staged/default')).toBe(true)
})
it('sets up default-partition policies on restore', async () => {
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
userAgentByPartition: {},
pendingCookieDbPath: null,
pendingCookieImports: {},
profiles: []
})
seedSitePermissions(fsState, { default: { 'https://example.com': { camera: 'allow' } } })
const {
sessionFromPartitionMock,
browserManagerHandleGuestWillDownloadMock,
browserManagerNotifyPermissionDeniedMock
} = installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.initializeBrowserSessionsFromPersistedState()
const defaultSessions = sessionFromPartitionMock.mock.results
.filter((_, idx) => sessionFromPartitionMock.mock.calls[idx]?.[0] === 'persist:orca-browser')
.map((r) => r.value)
expect(defaultSessions.length).toBeGreaterThan(0)
const defaultSession = defaultSessions[0]
const requestHandler = defaultSession.setPermissionRequestHandler.mock.calls[0][0]
const checkHandler = defaultSession.setPermissionCheckHandler.mock.calls[0][0]
const guestWc = { id: 401, getURL: vi.fn(() => 'https://example.com/account') }
const permissionCallback = vi.fn()
const decisions = new Map<string, boolean>()
const request = (
permission: string,
details: Partial<Electron.PermissionRequest & Electron.MediaAccessPermissionRequest> = {}
): void =>
requestHandler(
guestWc,
permission,
(allowed: boolean) => decisions.set(permission, allowed),
details
)
// Only camera has a saved answer; this guest has no prompt window.
request('fullscreen')
request('clipboard-read')
request('clipboard-sanitized-write')
request('notifications')
request('persistent-storage')
request('geolocation')
request('media', { mediaTypes: ['video'] })
await vi.waitFor(() => expect(decisions.size).toBe(7))
expect(Object.fromEntries(decisions)).toEqual({
fullscreen: true,
'clipboard-read': false,
'clipboard-sanitized-write': true,
notifications: false,
'persistent-storage': true,
geolocation: false,
media: true
})
expect(browserManagerNotifyPermissionDeniedMock).not.toHaveBeenCalled()
requestHandler(guestWc, 'mediaKeySystem', permissionCallback, {
requestingUrl: 'https://widget.example.net/embed',
isMainFrame: false
})
expect(browserManagerNotifyPermissionDeniedMock).toHaveBeenCalledWith({
guestWebContentsId: 401,
permission: 'mediaKeySystem',
rawUrl: 'https://widget.example.net/embed'
})
browserManagerNotifyPermissionDeniedMock.mockClear()
requestHandler(guestWc, 'mediaKeySystem', permissionCallback, { isMainFrame: true })
requestHandler(guestWc, 'mediaKeySystem', permissionCallback, {
requestingUrl: '',
isMainFrame: false
})
expect(
browserManagerNotifyPermissionDeniedMock.mock.calls.map(([args]) => args.rawUrl)
).toEqual(['https://example.com/account', 'https://example.com/account'])
browserManagerNotifyPermissionDeniedMock.mockClear()
requestHandler(guestWc, 'mediaKeySystem', permissionCallback, {
requestingUrl: 'about:blank',
isMainFrame: false
})
expect(browserManagerNotifyPermissionDeniedMock).toHaveBeenCalledWith({
guestWebContentsId: 401,
permission: 'mediaKeySystem',
rawUrl: ''
})
expect(checkHandler(null, 'fullscreen', '')).toBe(true)
expect(checkHandler(null, 'clipboard-read', 'https://example.com')).toBe(false)
expect(checkHandler(null, 'clipboard-sanitized-write', '')).toBe(true)
expect(checkHandler(null, 'notifications', 'https://example.com')).toBe(false)
expect(checkHandler(null, 'persistent-storage', '')).toBe(true)
expect(checkHandler(null, 'geolocation', 'https://example.com')).toBe(false)
expect(checkHandler(guestWc, 'media', '', { mediaType: 'video' })).toBe(true)
expect(checkHandler(guestWc, 'media', '', { mediaType: 'audio' })).toBe(false)
requestHandler(guestWc, 'storage-access', permissionCallback, {})
expect(permissionCallback).toHaveBeenLastCalledWith(true)
expect(checkHandler(null, 'storage-access', '')).toBe(true)
// Related Website Sets gate top-level storage access; Orca has no such data source.
requestHandler(guestWc, 'top-level-storage-access', permissionCallback, {})
expect(permissionCallback).toHaveBeenLastCalledWith(false)
expect(checkHandler(null, 'top-level-storage-access', '')).toBe(false)
expect(
browserManagerNotifyPermissionDeniedMock.mock.calls.map(([args]) => args.permission)
).toEqual(['mediaKeySystem', 'top-level-storage-access'])
expect(defaultSession.setDisplayMediaRequestHandler).toHaveBeenCalled()
const displayMediaHandler = defaultSession.setDisplayMediaRequestHandler.mock.calls[0][0]
const displayMediaCallback = vi.fn()
displayMediaHandler(null, displayMediaCallback)
expect(displayMediaCallback).toHaveBeenCalledWith({ video: undefined, audio: undefined })
const devicePermissionHandler = defaultSession.setDevicePermissionHandler.mock.calls[0][0]
expect(
devicePermissionHandler({
deviceType: 'hid',
origin: 'https://github.com',
device: { collections: [{ usagePage: 0xf1d0 }] }
})
).toBe(true)
expect(checkHandler(null, 'hid', '', { securityOrigin: 'https://github.com' })).toBe(true)
const selectHidHandler = defaultSession.on.mock.calls.find(
([eventName]: unknown[]) => eventName === 'select-hid-device'
)?.[1] as (
event: { preventDefault: () => void },
details: {
deviceList: { deviceId: string; collections?: { usagePage?: number }[] }[]
frame: { url: string }
},
callback: (deviceId?: string) => void
) => void
const hidCallback = vi.fn()
selectHidHandler(
{ preventDefault: vi.fn() },
{
frame: { url: 'https://github.com' },
deviceList: [
{ deviceId: 'keyboard', collections: [{ usagePage: 1 }] },
{ deviceId: 'security-key', collections: [{ usagePage: 0xf1d0 }] }
]
},
hidCallback
)
expect(hidCallback).toHaveBeenCalledWith('security-key')
const selectWebAuthnHandler = defaultSession.on.mock.calls.find(
([eventName]: unknown[]) => eventName === 'select-webauthn-account'
)?.[1] as (
event: { preventDefault: () => void },
details: { accounts: { credentialId: string }[] },
callback: (credentialId?: string | null) => void
) => void
const webAuthnCallback = vi.fn()
selectWebAuthnHandler(
{ preventDefault: vi.fn() },
{ accounts: [{ credentialId: 'credential-1' }] },
webAuthnCallback
)
expect(webAuthnCallback).toHaveBeenCalledWith('credential-1')
const willDownloadHandler = defaultSession.on.mock.calls.find(
([eventName]: unknown[]) => eventName === 'will-download'
)?.[1] as (
event: unknown,
item: { getFilename: () => string },
webContents: { id: number }
) => void
expect(willDownloadHandler).toBeTypeOf('function')
const item = { getFilename: vi.fn(() => 'report.pdf') }
willDownloadHandler({}, item, { id: 402 })
expect(browserManagerHandleGuestWillDownloadMock).toHaveBeenCalledWith({
guestWebContentsId: 402,
item
})
})
it('does not stack default-partition policy handlers on repeated restore', async () => {
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
userAgentByPartition: {},
pendingCookieDbPath: null,
pendingCookieImports: {},
profiles: []
})
const { sessionFromPartitionMock } = installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.initializeBrowserSessionsFromPersistedState()
browserSessionRegistry.initializeBrowserSessionsFromPersistedState()
const defaultSessions = sessionFromPartitionMock.mock.results
.filter((_, idx) => sessionFromPartitionMock.mock.calls[idx]?.[0] === 'persist:orca-browser')
.map((r) => r.value)
const policySessions = defaultSessions.filter(
(s) => s.setPermissionRequestHandler.mock.calls.length > 0
)
expect(policySessions).toHaveLength(1)
expect(
policySessions[0].on.mock.calls.filter(
([eventName]: unknown[]) => eventName === 'will-download'
)
).toHaveLength(1)
expect(
policySessions[0].on.mock.calls.filter(
([eventName]: unknown[]) => eventName === 'select-hid-device'
)
).toHaveLength(1)
expect(
policySessions[0].on.mock.calls.filter(
([eventName]: unknown[]) => eventName === 'select-webauthn-account'
)
).toHaveLength(1)
})
it('notifies when default-partition media permission is denied', async () => {
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
userAgentByPartition: {},
pendingCookieDbPath: null,
pendingCookieImports: {},
profiles: []
})
seedSitePermissions(fsState, { default: { 'https://example.com': { camera: 'allow' } } })
const {
sessionFromPartitionMock,
browserManagerNotifyPermissionDeniedMock,
requestSystemMediaAccessMock
} = installModuleMocks(fsState)
requestSystemMediaAccessMock.mockResolvedValue(false)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.initializeBrowserSessionsFromPersistedState()
const defaultSession = sessionFromPartitionMock.mock.results.find(
(_, idx) => sessionFromPartitionMock.mock.calls[idx]?.[0] === 'persist:orca-browser'
)?.value
const requestHandler = defaultSession.setPermissionRequestHandler.mock.calls[0][0]
const guestWc = { id: 403, getURL: vi.fn(() => 'https://example.com/camera') }
const callback = vi.fn()
requestHandler(guestWc, 'media', callback, { mediaTypes: ['video'] })
guestWc.getURL.mockReturnValue('https://example.com/after-navigation')
await vi.waitFor(() => expect(callback).toHaveBeenCalledWith(false))
expect(browserManagerNotifyPermissionDeniedMock).toHaveBeenCalledWith({
guestWebContentsId: 403,
permission: 'media',
rawUrl: 'https://example.com/camera'
})
})
it('expires an old unmarked stage and removes unrelated missing entries', async () => {
const importedPartition = 'persist:orca-browser-session-22222222-2222-4222-8222-222222222222'
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
userAgentByPartition: {},
pendingCookieDbPath: null,
pendingCookieImports: {
[importedPartition]: '/staged/imported',
'persist:orca-browser': '/staged/missing'
},
profiles: [
{
id: '22222222-2222-4222-8222-222222222222',
scope: 'imported',
partition: importedPartition,
label: 'Imported',
source: { browserFamily: 'comet', importedAt: 1 }
}
]
})
fsState.present.add('/staged/imported')
installModuleMocks(fsState, new Set(['/staged/imported']))
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.applyPendingCookieImport()
const written = JSON.parse(fsState.files.get(META_PATH) ?? '{}')
expect(written.pendingCookieImports).toEqual({})
expect(written.pendingCookieDbPath).toBeNull()
})
it('ignores pending cookie imports for invalid persisted profile partitions', async () => {
const invalidPartition = 'persist:../../outside'
const fsState = createFsState()
seedMeta(fsState, {
defaultSource: null,
userAgent: null,
userAgentByPartition: {},
pendingCookieDbPath: null,
pendingCookieImports: {
[invalidPartition]: '/staged/evil'
},
profiles: [
{
id: 'profile-1',
scope: 'imported',
partition: invalidPartition,
label: 'Invalid',
source: null
}
]
})
fsState.present.add('/staged/evil')
installModuleMocks(fsState)
const { browserSessionRegistry } = await import('./browser-session-registry')
browserSessionRegistry.applyPendingCookieImport()
const written = JSON.parse(fsState.files.get(META_PATH) ?? '{}')
expect(written.pendingCookieImports).toEqual({})
expect(fsState.present.has('/outside/Cookies')).toBe(false)
})
})
@@ -1,7 +1,7 @@
import { mkdtempSync } from 'node:fs'
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const {
sessionFromPartitionMock,
@@ -69,20 +69,24 @@ import {
listBrowserSitePermissions,
setBrowserSitePermissionDecisions
} from '../site-settings/browser-site-permission-store'
import { googleAuthUserAgent } from '../identity/browser-google-auth-ua'
import { installBrowserSessionUserAgentPolicy } from '../identity/browser-session-ua'
import { setBrowserNetworkProxySettingsResolver } from './browser-session-proxy'
import { handleElectronProxyLogin } from '../../network/electron-proxy-credentials'
import { applyProxySettingsToSession } from '../../network/proxy-settings'
import { ORCA_BROWSER_PARTITION } from '../../../shared/constants'
import {
DEFAULT_LOCAL_ORCA_PROFILE_ID,
getOrcaProfileBrowserDefaultPartition,
getOrcaProfileBrowserSessionPartition
} from '../../../shared/orca-profiles'
import { DEFAULT_LOCAL_ORCA_PROFILE_ID } from '../../../shared/orca-profiles'
describe('BrowserSessionRegistry', () => {
let profileDirectory: string
afterEach(() => {
rmSync(profileDirectory, { recursive: true, force: true })
})
beforeEach(() => {
profileDirectory = mkdtempSync(join(tmpdir(), 'orca-session-registry-'))
browserSessionRegistry.configureForOrcaProfile({
orcaProfileId: DEFAULT_LOCAL_ORCA_PROFILE_ID,
profileDirectory
})
sessionFromPartitionMock.mockReset()
askForMediaAccessMock.mockReset()
getMediaAccessStatusMock.mockReset()
@@ -108,84 +112,6 @@ describe('BrowserSessionRegistry', () => {
})
})
it('has a default profile on construction', () => {
const defaultProfile = browserSessionRegistry.getDefaultProfile()
expect(defaultProfile.id).toBe('default')
expect(defaultProfile.scope).toBe('default')
expect(defaultProfile.partition).toBe(ORCA_BROWSER_PARTITION)
})
it.each([10, 100])(
'resolves only known actual Sessions with linear profile work at %s',
(population) => {
const directory = mkdtempSync(join(tmpdir(), 'orca-session-media-'))
browserSessionRegistry.configureForOrcaProfile({
orcaProfileId: DEFAULT_LOCAL_ORCA_PROFILE_ID,
profileDirectory: directory
})
const policySession = sessionFromPartitionMock()
const sessions = new Map<string, typeof policySession>()
sessionFromPartitionMock.mockImplementation((partition: string) => {
const known = sessions.get(partition)
if (!known) {
throw new Error('Lookup attempted to materialize an unknown Session')
}
return known
})
const profiles = Array.from({ length: population }, (_, index) => {
const id = `00000000-0000-0000-0000-${String(index + 1).padStart(12, '0')}`
const partition = getOrcaProfileBrowserSessionPartition(DEFAULT_LOCAL_ORCA_PROFILE_ID, id)
sessions.set(partition, { ...policySession })
return {
id,
partition,
scope: 'isolated' as const,
label: `Profile ${index}`,
source: null
}
})
const defaultProfile = browserSessionRegistry.getDefaultProfile()
sessions.set(defaultProfile.partition, { ...policySession })
browserSessionRegistry.hydrateFromPersisted(profiles)
sessionFromPartitionMock.mockClear()
expect(
browserSessionRegistry.profileIdForSession(sessions.get(defaultProfile.partition)!)
).toBe('default')
expect(sessionFromPartitionMock).toHaveBeenCalledTimes(1)
sessionFromPartitionMock.mockClear()
const last = profiles.at(-1)!
expect(browserSessionRegistry.profileIdForSession(sessions.get(last.partition)!)).toBe(
last.id
)
expect(sessionFromPartitionMock).toHaveBeenCalledTimes(population + 1)
sessionFromPartitionMock.mockClear()
expect(browserSessionRegistry.profileIdForSession({ ...policySession })).toBeNull()
expect(sessionFromPartitionMock).toHaveBeenCalledTimes(population + 1)
browserSessionRegistry.configureForOrcaProfile({
orcaProfileId: DEFAULT_LOCAL_ORCA_PROFILE_ID,
profileDirectory: directory
})
}
)
it('allows the default partition', () => {
expect(browserSessionRegistry.isAllowedPartition(ORCA_BROWSER_PARTITION)).toBe(true)
})
it('rejects unknown partitions', () => {
expect(browserSessionRegistry.isAllowedPartition('persist:evil-partition')).toBe(false)
})
it('creates an isolated profile with a unique partition', async () => {
const profile = await browserSessionRegistry.createProfile('isolated', 'Test Isolated')
expect(profile).not.toBeNull()
expect(profile!.scope).toBe('isolated')
expect(profile!.partition).toMatch(/^persist:orca-browser-session-/)
expect(profile!.partition).not.toBe(ORCA_BROWSER_PARTITION)
expect(profile!.label).toBe('Test Isolated')
expect(profile!.source).toBeNull()
})
it('does not return a runtime profile until its proxy is ready', async () => {
let finishWrite: (() => void) | undefined
let proxyReady = false
@@ -282,79 +208,6 @@ describe('BrowserSessionRegistry', () => {
).rejects.toThrow('retired')
})
it('rejects creating a profile with scope default', async () => {
const profile = await browserSessionRegistry.createProfile('default', 'Sneaky')
expect(profile).toBeNull()
})
it('allows created profile partitions', async () => {
const profile = await browserSessionRegistry.createProfile('isolated', 'Allowed')
expect(profile).not.toBeNull()
expect(browserSessionRegistry.isAllowedPartition(profile!.partition)).toBe(true)
})
it('creates an imported profile', async () => {
const profile = await browserSessionRegistry.createProfile('imported', 'My Import')
expect(profile).not.toBeNull()
expect(profile!.scope).toBe('imported')
expect(profile!.partition).toMatch(/^persist:orca-browser-session-/)
})
it('resolves partition for a known profile', async () => {
const profile = await browserSessionRegistry.createProfile('isolated', 'Resolve Test')
expect(profile).not.toBeNull()
expect(browserSessionRegistry.resolvePartition(profile!.id)).toBe(profile!.partition)
})
it('resolves default partition for null/undefined profileId', () => {
expect(browserSessionRegistry.resolvePartition(null)).toBe(ORCA_BROWSER_PARTITION)
expect(browserSessionRegistry.resolvePartition(undefined)).toBe(ORCA_BROWSER_PARTITION)
})
it('resolves default partition for unknown profileId', () => {
expect(browserSessionRegistry.resolvePartition('nonexistent')).toBe(ORCA_BROWSER_PARTITION)
})
it('strictly resolves known profile partitions without downgrading unknown profiles', async () => {
const profile = await browserSessionRegistry.createProfile('isolated', 'Strict Resolve')
expect(profile).not.toBeNull()
expect(browserSessionRegistry.resolveKnownPartition(null)).toBe(ORCA_BROWSER_PARTITION)
expect(browserSessionRegistry.resolveKnownPartition(undefined)).toBe(ORCA_BROWSER_PARTITION)
expect(browserSessionRegistry.resolveKnownPartition('default')).toBe(ORCA_BROWSER_PARTITION)
expect(browserSessionRegistry.resolveKnownPartition(profile!.id)).toBe(profile!.partition)
expect(browserSessionRegistry.resolveKnownPartition('missing-profile')).toBeNull()
})
it('lists all profiles', async () => {
const before = browserSessionRegistry.listProfiles().length
await browserSessionRegistry.createProfile('isolated', 'List Test')
const after = browserSessionRegistry.listProfiles()
expect(after.length).toBe(before + 1)
})
it('updates profile source', async () => {
const profile = await browserSessionRegistry.createProfile('imported', 'Source Test')
expect(profile).not.toBeNull()
const updated = browserSessionRegistry.updateProfileSource(profile!.id, {
browserFamily: 'edge',
importedAt: Date.now()
})
expect(updated).not.toBeNull()
expect(updated!.source?.browserFamily).toBe('edge')
})
it('updates profile source with comet family', async () => {
const profile = await browserSessionRegistry.createProfile('imported', 'Comet Source Test')
expect(profile).not.toBeNull()
const updated = browserSessionRegistry.updateProfileSource(profile!.id, {
browserFamily: 'comet',
importedAt: Date.now()
})
expect(updated).not.toBeNull()
expect(updated!.source?.browserFamily).toBe('comet')
})
it('deletes a non-default profile and its remembered site answers', async () => {
configureBrowserSitePermissionStore(mkdtempSync(join(tmpdir(), 'orca-site-permissions-')))
const profile = await browserSessionRegistry.createProfile('isolated', 'Delete Test')
@@ -368,63 +221,7 @@ describe('BrowserSessionRegistry', () => {
expect(listBrowserSitePermissions(profile!.id)).toEqual([])
})
it('keeps a profile whose saved logins could not be deleted', async () => {
const profile = await browserSessionRegistry.createProfile('isolated', 'Vault Stuck')
forgetBrowserPasswordVaultMock.mockReturnValueOnce(false)
await expect(browserSessionRegistry.deleteProfile(profile!.id)).resolves.toBe(false)
expect(browserSessionRegistry.getProfile(profile!.id)).not.toBeNull()
})
it('retains session security policies when deleting a profile', async () => {
const profile = await browserSessionRegistry.createProfile('isolated', 'Policy Delete Test')
expect(profile).not.toBeNull()
const mockSession = sessionFromPartitionMock.mock.results[0]?.value
const permissionWrites = mockSession.setPermissionRequestHandler.mock.calls.length
const downloadListenerWrites = mockSession.removeListener.mock.calls.length
await expect(browserSessionRegistry.deleteProfile(profile!.id)).resolves.toBe(true)
expect(mockSession.setPermissionRequestHandler).toHaveBeenCalledTimes(permissionWrites)
expect(mockSession.removeListener).toHaveBeenCalledTimes(downloadListenerWrites)
expect(removeCertificateRequestGuardMock).not.toHaveBeenCalled()
})
// Electron Sessions outlive deleted profiles; their header hooks must be retired.
it('retires the user agent policy when deleting a profile', async () => {
const profile = await browserSessionRegistry.createProfile('isolated', 'UA Delete Test')
const mockSession = sessionFromPartitionMock.mock.results[0]?.value
expect(mockSession.webRequest.onBeforeSendHeaders).toHaveBeenCalledWith(
expect.anything(),
expect.any(Function)
)
await expect(browserSessionRegistry.deleteProfile(profile!.id)).resolves.toBe(true)
expect(mockSession.webRequest.onBeforeSendHeaders).toHaveBeenLastCalledWith(null)
})
it('keeps the request guard installed while deleted-profile guests remain', async () => {
setBrowserNetworkProxySettingsResolver(() => ({
httpProxyUrl: 'http://proxy.example:8080',
httpProxyBypassRules: ''
}))
const profile = await browserSessionRegistry.createProfile('isolated', 'Delayed Delete')
const mockSession = sessionFromPartitionMock.mock.results[0]?.value
let finishClose: (() => void) | undefined
mockSession.closeAllConnections.mockImplementationOnce(
() => new Promise<void>((resolve) => (finishClose = resolve))
)
removeCertificateRequestGuardMock.mockClear()
const deletion = browserSessionRegistry.deleteProfile(profile!.id)
await vi.waitFor(() => expect(mockSession.setProxy).toHaveBeenCalledWith({ mode: 'system' }))
expect(removeCertificateRequestGuardMock).not.toHaveBeenCalled()
finishClose?.()
await expect(deletion).resolves.toBe(true)
expect(removeCertificateRequestGuardMock).not.toHaveBeenCalled()
})
it('creates one managed profile per owner and a fresh one after its owner is removed', async () => {
const owner = { kind: 'worktree' as const, id: 'repo-1::/work/feature' }
@@ -476,301 +273,4 @@ describe('BrowserSessionRegistry', () => {
browserSessionRegistry.onProfilesChanged = null
}
})
it('refuses to delete the default profile', async () => {
const deleted = await browserSessionRegistry.deleteProfile('default')
expect(deleted).toBe(false)
expect(browserSessionRegistry.getDefaultProfile()).not.toBeNull()
})
it('hydrates profiles from persisted data', () => {
const fakeProfile = {
id: '00000000-0000-0000-0000-000000000001',
scope: 'imported' as const,
partition: 'persist:orca-browser-session-00000000-0000-0000-0000-000000000001',
label: 'Hydrated',
source: { browserFamily: 'manual' as const, importedAt: 1000 }
}
browserSessionRegistry.hydrateFromPersisted([fakeProfile])
expect(browserSessionRegistry.getProfile('00000000-0000-0000-0000-000000000001')).not.toBeNull()
expect(browserSessionRegistry.isAllowedPartition(fakeProfile.partition)).toBe(true)
})
it('rejects a persisted profile whose partition belongs to a different profile id', () => {
const profileId = '00000000-0000-4000-8000-000000000021'
const claimedPartition = 'persist:orca-browser-session-00000000-0000-4000-8000-000000000022'
browserSessionRegistry.hydrateFromPersisted([
{
id: profileId,
scope: 'isolated',
partition: claimedPartition,
label: 'Conflicting identity',
source: null
}
])
expect(browserSessionRegistry.getProfile(profileId)).toBeNull()
expect(browserSessionRegistry.isAllowedPartition(claimedPartition)).toBe(false)
})
it('applies and clears existing browser-profile policy on an opaque route partition', async () => {
const partition =
'persist:orca-browser-v1-1111111111111111222222222222222233333333333333334444444444444444'
setBrowserNetworkProxySettingsResolver(() => ({
httpProxyUrl: 'http://app-proxy.example:8080',
httpProxyBypassRules: ''
}))
browserSessionRegistry.setupRoutePartitionPolicies(partition, 'default')
expect(sessionFromPartitionMock).toHaveBeenCalledWith(partition)
const configuredSession = sessionFromPartitionMock.mock.results[0]?.value
expect(configuredSession.setPermissionRequestHandler).toHaveBeenCalled()
expect(configuredSession.setPermissionCheckHandler).toHaveBeenCalled()
await new Promise<void>((resolve) => setImmediate(resolve))
expect(configuredSession.setProxy).not.toHaveBeenCalled()
browserSessionRegistry.clearRoutePartitionPolicies(partition)
const clearedSession = sessionFromPartitionMock.mock.results.at(-1)?.value
expect(clearedSession.setPermissionRequestHandler).toHaveBeenCalledWith(null)
expect(clearedSession.setPermissionCheckHandler).toHaveBeenCalledWith(null)
})
it('rejects route partitions for missing browser profiles', () => {
const partition =
'persist:orca-browser-v1-aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd'
expect(() =>
browserSessionRegistry.setupRoutePartitionPolicies(partition, 'missing-profile')
).toThrow('browser_route_partition_profile_unavailable')
})
it('auto-grants pointer lock for browser partitions', async () => {
await browserSessionRegistry.createProfile('isolated', 'Pointer Lock Test')
const mockSession = sessionFromPartitionMock.mock.results[0]?.value
const requestHandler = mockSession.setPermissionRequestHandler.mock.calls[0][0]
const checkHandler = mockSession.setPermissionCheckHandler.mock.calls[0][0]
const callback = vi.fn()
const guestWc = { id: 7, getURL: vi.fn(() => 'https://example.com/') }
requestHandler(guestWc, 'pointerLock', callback, {})
expect(callback).toHaveBeenCalledWith(true)
expect(checkHandler(null, 'pointerLock', '', {})).toBe(true)
})
it('asks macOS for the camera only after the user allowed the site', async () => {
configureBrowserSitePermissionStore(mkdtempSync(join(tmpdir(), 'orca-site-permissions-')))
const profile = await browserSessionRegistry.createProfile('isolated', 'Media Test')
if (!profile) {
throw new Error('Expected the profile')
}
setBrowserSitePermissionDecisions(profile.id, 'https://example.com', ['camera'], 'allow')
const mockSession = sessionFromPartitionMock.mock.results[0]?.value
const requestHandler = mockSession.setPermissionRequestHandler.mock.calls[0][0]
const checkHandler = mockSession.setPermissionCheckHandler.mock.calls[0][0]
const allowedSite = vi.fn()
const otherSite = vi.fn()
const allowedGuest = { id: 7, getURL: vi.fn(() => 'https://example.com/') }
const otherGuest = { id: 8, getURL: vi.fn(() => 'https://other.test/') }
requestHandler(allowedGuest, 'media', allowedSite, { mediaTypes: ['video'] })
requestHandler(otherGuest, 'media', otherSite, { mediaTypes: ['video'] })
await vi.waitFor(() => expect(allowedSite).toHaveBeenCalledWith(true))
await vi.waitFor(() => expect(otherSite).toHaveBeenCalledWith(false))
expect(checkHandler(allowedGuest, 'media', '', { mediaType: 'video' })).toBe(true)
expect(checkHandler(otherGuest, 'media', '', { mediaType: 'video' })).toBe(false)
expect(checkHandler(null, 'notifications', 'https://example.com', {})).toBe(false)
expect(checkHandler(null, 'persistent-storage', '', {})).toBe(true)
expect(checkHandler(null, 'geolocation', 'https://example.com', {})).toBe(false)
})
it('wires WebAuthn device selection for isolated partitions', async () => {
await browserSessionRegistry.createProfile('isolated', 'Security Key Test')
const mockSession = sessionFromPartitionMock.mock.results[0]?.value
const devicePermissionHandler = mockSession.setDevicePermissionHandler.mock.calls[0][0]
const checkHandler = mockSession.setPermissionCheckHandler.mock.calls[0][0]
expect(
devicePermissionHandler({
deviceType: 'hid',
origin: 'https://github.com',
device: { collections: [{ usagePage: 0xf1d0 }] }
})
).toBe(true)
expect(
devicePermissionHandler({
deviceType: 'hid',
origin: 'http://[::1]:5173',
device: { collections: [{ usagePage: 0xf1d0 }] }
})
).toBe(true)
expect(
devicePermissionHandler({
deviceType: 'hid',
origin: 'https://github.com',
device: { collections: [{ usagePage: 1 }] }
})
).toBe(false)
expect(checkHandler(null, 'hid', '', { securityOrigin: 'https://github.com' })).toBe(true)
const selectHidHandler = mockSession.on.mock.calls.find(
([eventName]) => eventName === 'select-hid-device'
)?.[1]
const hidCallback = vi.fn()
selectHidHandler(
{ preventDefault: vi.fn() },
{
frame: { url: 'https://github.com' },
deviceList: [
{ deviceId: 'keyboard', collections: [{ usagePage: 1 }] },
{ deviceId: 'security-key', collections: [{ usagePage: 0xf1d0 }] }
]
},
hidCallback
)
expect(hidCallback).toHaveBeenCalledWith('security-key')
const selectWebAuthnHandler = mockSession.on.mock.calls.find(
([eventName]) => eventName === 'select-webauthn-account'
)?.[1]
const webAuthnCallback = vi.fn()
selectWebAuthnHandler(
{ preventDefault: vi.fn() },
{ accounts: [{ credentialId: 'credential-1' }] },
webAuthnCallback
)
expect(webAuthnCallback).toHaveBeenCalledWith('credential-1')
})
it('uses profile-owned partitions for non-default Orca profiles', async () => {
const orcaProfileId = 'local-work'
browserSessionRegistry.configureForOrcaProfile({
orcaProfileId,
profileDirectory: '/profiles/local-work'
})
expect(browserSessionRegistry.getDefaultProfile().partition).toBe(
getOrcaProfileBrowserDefaultPartition(orcaProfileId)
)
expect(browserSessionRegistry.isAllowedPartition(ORCA_BROWSER_PARTITION)).toBe(false)
const profile = await browserSessionRegistry.createProfile('isolated', 'Work Browser')
expect(profile).not.toBeNull()
expect(profile!.partition).toBe(
getOrcaProfileBrowserSessionPartition(orcaProfileId, profile!.id)
)
browserSessionRegistry.configureForOrcaProfile({
orcaProfileId: DEFAULT_LOCAL_ORCA_PROFILE_ID,
profileDirectory: '/profiles/local-default'
})
})
describe('installBrowserSessionUserAgentPolicy', () => {
function install(): (details: unknown, callback: ReturnType<typeof vi.fn>) => void {
const onBeforeSendHeaders = vi.fn()
installBrowserSessionUserAgentPolicy(
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the hook reads only the mocked webRequest member exercised here.
{ webRequest: { onBeforeSendHeaders } } as never,
(request) =>
request.currentUserAgent === googleAuthUserAgent()
? { kind: 'google-auth', userAgent: googleAuthUserAgent() }
: undefined
)
expect(onBeforeSendHeaders).toHaveBeenCalledWith(
{ urls: ['http://*/*', 'https://*/*', 'ws://*/*', 'wss://*/*'] },
expect.any(Function)
)
return onBeforeSendHeaders.mock.calls[0][1]
}
it('leaves ordinary-host identity headers untouched', () => {
const callback = vi.fn()
install()(
{
url: 'https://example.com/',
requestHeaders: {
'User-Agent': 'Mozilla/5.0 Chrome/150.0.0.0 Safari/537.36',
'sec-ch-ua': 'browser-owned',
Cookie: 'abc=123'
}
},
callback
)
expect(callback.mock.calls[0][0].requestHeaders).toEqual({
'User-Agent': 'Mozilla/5.0 Chrome/150.0.0.0 Safari/537.36',
'sec-ch-ua': 'browser-owned',
Cookie: 'abc=123'
})
})
it("presents Electron's own UA on Google auth hosts and keeps its client hints", () => {
const callback = vi.fn()
install()(
{
url: 'https://accounts.google.com/v3/signin/identifier',
requestHeaders: {
'User-Agent': 'Mozilla/5.0 Chrome/150.0.0.0 Safari/537.36',
'sec-ch-ua': 'browser-owned',
Accept: 'text/html'
}
},
callback
)
expect(callback.mock.calls[0][0].requestHeaders).toEqual({
'User-Agent': googleAuthUserAgent(),
'sec-ch-ua': 'browser-owned',
Accept: 'text/html'
})
})
it('drops mobile-preset client hints on Google auth hosts', () => {
const callback = vi.fn()
install()(
{
url: 'https://accounts.google.com/gsi/button',
resourceType: 'subFrame',
requestHeaders: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) CriOS/150.0.0.0',
'sec-ch-ua': '"Google Chrome";v="150"',
'sec-ch-ua-mobile': '?1',
'sec-ch-ua-platform': '"iOS"'
}
},
callback
)
expect(callback.mock.calls[0][0].requestHeaders).toEqual({
'User-Agent': googleAuthUserAgent()
})
})
it("keeps a request already carrying Electron's UA untouched on Google auth hosts", () => {
const headers = { 'User-Agent': googleAuthUserAgent(), 'sec-ch-ua': 'browser-owned' }
const callback = vi.fn()
install()(
{ url: 'https://accounts.google.com/v3/signin/identifier', requestHeaders: { ...headers } },
callback
)
expect(callback.mock.calls[0][0].requestHeaders).toEqual(headers)
})
it('keeps the session identity on Google app subdomains', () => {
const callback = vi.fn()
install()(
{
url: 'https://myaccount.google.com/',
requestHeaders: { 'User-Agent': 'Chrome/150', 'sec-ch-ua': 'browser-owned' }
},
callback
)
expect(callback.mock.calls[0][0].requestHeaders).toEqual({
'User-Agent': 'Chrome/150',
'sec-ch-ua': 'browser-owned'
})
})
})
})
@@ -176,44 +176,6 @@ describe('BrowserSiteDisplayController color scheme', () => {
expect(preview.debugger.isAttached()).toBe(false)
})
it.each([10, 100])('sends one media update per eligible guest at population %s', (population) => {
const controller = new BrowserSiteDisplayController()
const ordinary = fakeGuest('https://ordinary.example/')
controller.onGuestRegistered(ordinary, null)
const guests = Array.from({ length: population }, () =>
fakeGuest('orca-preview://grant/a.html')
)
for (const guest of guests) {
controller.onGuestDocumentStart(guest, {
url: guest.url,
profileId: null,
colorScheme: 'dark'
})
controller.onGuestDocumentStart(guest, {
url: guest.url,
profileId: null,
colorScheme: 'dark'
})
expect(guest.mediaSent).toEqual(['dark', 'dark'])
expect(guest.debugger.listenerCount('detach')).toBe(1)
guest.mediaSent.length = 0
}
for (const guest of guests) {
controller.setGuestColorSchemeFallback(guest, 'light')
}
expect(guests.reduce((total, guest) => total + guest.mediaSent.length, 0)).toBe(population)
for (const guest of guests) {
controller.setGuestColorSchemeFallback(guest, 'light')
controller.onGuestRetired(guest.id)
expect(guest.debugger.isAttached()).toBe(false)
expect(guest.debugger.listenerCount('detach')).toBe(0)
controller.setGuestColorSchemeFallback(guest, 'dark')
}
expect(guests.reduce((total, guest) => total + guest.mediaSent.length, 0)).toBe(population)
expect(ordinary.mediaSent).toEqual([])
expect(ordinary.debugger.isAttached()).toBe(false)
})
it.each([
{ order: 'read-first', profileId: 'work', colorScheme: 'light', saved: 'dark' },
{ order: 'registration-first', profileId: 'work', colorScheme: 'light', saved: 'dark' },
@@ -261,48 +223,6 @@ describe('BrowserSiteDisplayController color scheme', () => {
expect(guest.debugger.listenerCount('detach')).toBe(0)
}
)
it('sets the site scheme as each page commits and clears it once on leaving', () => {
const controller = new BrowserSiteDisplayController()
const guest = fakeGuest('https://github.com/')
controller.onGuestRegistered(guest, null)
controller.setColorScheme(guest.id, 'dark')
controller.zoomPage(guest.id, 'in')
commit(controller, guest, 'https://docs.example.com/')
commit(controller, guest, 'https://news.example.com/')
commit(controller, guest, 'https://github.com/x')
expect(guest.mediaSent).toEqual(['dark', '', 'dark'])
})
it('gives a restored tab its scheme when its first load started before it registered', () => {
const controller = new BrowserSiteDisplayController()
const first = fakeGuest('https://github.com/')
controller.onGuestRegistered(first, null)
controller.setColorScheme(first.id, 'light')
const restored = fakeGuest('')
controller.onGuestRegistered(restored, null)
restored.url = 'https://github.com/restored'
controller.onMainFrameNavigationCommitted(restored.id)
expect(restored.mediaSent).toEqual(['light'])
})
it('attaches again after DevTools detaches the debugger', () => {
const controller = new BrowserSiteDisplayController()
const guest = fakeGuest('https://github.com/')
controller.onGuestRegistered(guest, null)
controller.setColorScheme(guest.id, 'dark')
guest.debugger.detach()
expect(guest.debugger.isAttached()).toBe(false)
commit(controller, guest, 'https://github.com/next')
expect(guest.debugger.isAttached()).toBe(true)
expect(guest.mediaSent.at(-1)).toBe('dark')
})
})
vi.mock('electron', () => ({ app: { getPath: () => tmpdir() } }))
@@ -126,25 +126,6 @@ describe('browser site permission prompts', () => {
vi.useRealTimers()
})
it('asks once for camera and microphone together, then remembers Allow', async () => {
const call = ask(['camera', 'microphone'])
expect(sentRequests(renderer)).toMatchObject([
{
kind: 'site-permission',
details: {
origin: 'https://meet.example.com',
permissions: ['camera', 'microphone']
}
}
])
await answerLatest('allow')
await expect(call).resolves.toBe(true)
await expect(ask(['camera'])).resolves.toBe(true)
expect(sentRequests(renderer)).toHaveLength(1)
expect(allowedOnCheck(['camera', 'microphone'])).toBe(true)
})
it('asks only for what is unanswered, and a remembered Block refuses the whole request', async () => {
const camera = ask(['camera'])
await answerLatest('allow')
@@ -158,25 +139,6 @@ describe('browser site permission prompts', () => {
expect(sentRequests(renderer)).toHaveLength(2)
})
it('forgets a dismissed prompt, so the site is asked again later', async () => {
const call = ask(['notifications'])
await answerLatest(null)
await expect(call).resolves.toBe(false)
expect(listBrowserSitePermissions('default')).toEqual([])
void ask(['notifications'])
expect(sentRequests(renderer)).toHaveLength(2)
})
it('keeps each browser profile to its own answers', async () => {
const call = ask(['clipboard'], { memory: savedBrowserSitePermissions('work') })
await answerLatest('allow')
await call
void ask(['clipboard'], { memory: savedBrowserSitePermissions('default') })
expect(sentRequests(renderer)).toHaveLength(2)
})
it("keeps a paired server's answers for its session only, where checks still see them", async () => {
const pairedSession = sessionBrowserSitePermissions('persist:paired-a')
const call = ask(['notifications'], { memory: pairedSession })
@@ -191,15 +153,6 @@ describe('browser site permission prompts', () => {
expect(sentRequests(renderer)).toHaveLength(2)
})
it("gives a frame the tab's answers for what the tab delegates", async () => {
const call = ask(['camera'])
await answerLatest('allow')
await call
await expect(ask(['camera'], { frameOrigin: 'https://video.example.net' })).resolves.toBe(true)
expect(sentRequests(renderer)).toHaveLength(1)
})
it("gives another site's frame only its own notification answer, and never asks for it", async () => {
const tab = ask(['notifications'])
await answerLatest('allow')
@@ -223,32 +176,6 @@ describe('browser site permission prompts', () => {
expect(sentRequests(renderer)).toHaveLength(2)
})
it('asks for a local file, and every local file shares the answer', async () => {
const call = ask(['camera'], { guest: mockGuest('file:///Users/me/lab/permission-lab.html') })
expect(sentRequests(renderer).at(-1)?.details.origin).toBe('file://')
await answerLatest('allow')
await expect(call).resolves.toBe(true)
await expect(
ask(['camera'], { guest: mockGuest('file:///Users/me/other/page.html') })
).resolves.toBe(true)
expect(sentRequests(renderer)).toHaveLength(1)
})
it('refuses an opaque page without asking', async () => {
await expect(ask(['camera'], { guest: mockGuest('data:text/html,<p>hi</p>') })).resolves.toBe(
false
)
expect(sentRequests(renderer)).toEqual([])
})
it('refuses without asking when no window can show the question', async () => {
mocks.resolvePagePromptTarget.mockReturnValue(null)
await expect(ask(['camera'])).resolves.toBe(false)
expect(sentRequests(renderer)).toEqual([])
})
it('remembers answers across a restart', async () => {
const directory = mkdtempSync(join(tmpdir(), 'orca-site-permissions-'))
configureBrowserSitePermissionStore(directory)
@@ -13,11 +13,8 @@ vi.mock('electron', () => ({
vi.mock('../browser-manager', () => ({
browserManager: { getRendererContextForGuest: getRendererContextForGuestMock }
}))
import {
BROWSER_WEBAUTHN_ACCOUNT_PICKER_TIMEOUT_MS,
cancelAllBrowserWebAuthnAccountRequests,
cancelBrowserWebAuthnAccountRequests,
requestBrowserWebAuthnAccount,
respondToBrowserWebAuthnAccountRequest
} from './browser-webauthn-account-picker'
@@ -59,32 +56,6 @@ describe('browser WebAuthn account picker', () => {
vi.useRealTimers()
})
it('returns the credential selected by the owning renderer', async () => {
const guest = mockWebContents(41)
const renderer = mockWebContents(42)
fromFrameMock.mockReturnValue(guest)
getRendererContextForGuestMock.mockReturnValue({ browserPageId: 'page-1', renderer })
const selection = requestBrowserWebAuthnAccount(accountDetails(), mockSession())
const request = vi.mocked(renderer.send).mock.calls[0][1]
expect(request).toMatchObject({
browserPageId: 'page-1',
relyingPartyId: 'accounts.example.com',
accounts: [
{ credentialId: 'credential-1', displayName: 'Personal', name: 'me@example.com' },
{ credentialId: 'credential-2', displayName: 'Work', name: 'me@work.example' }
]
})
expect(
respondToBrowserWebAuthnAccountRequest(renderer, {
requestId: request.requestId,
credentialId: 'credential-2'
})
).toBe(true)
await expect(selection).resolves.toBe('credential-2')
})
it('rejects responses from another renderer or with an unoffered credential', async () => {
const guest = mockWebContents(51)
const renderer = mockWebContents(52)
@@ -112,92 +83,6 @@ describe('browser WebAuthn account picker', () => {
await expect(selection).resolves.toBeNull()
})
it('cancels when the tab closes or the prompt times out', async () => {
const guest = mockWebContents(61)
const renderer = mockWebContents(62)
fromFrameMock.mockReturnValue(guest)
getRendererContextForGuestMock.mockReturnValue({ browserPageId: 'page-3', renderer })
const browserSession = mockSession()
const closedSelection = requestBrowserWebAuthnAccount(accountDetails(), browserSession)
vi.mocked(renderer.send).mockImplementation((channel) => {
if (channel === 'browser:webauthn-account-request-closed') {
throw new Error('renderer destroyed during send')
}
})
cancelBrowserWebAuthnAccountRequests('page-3')
await expect(closedSelection).resolves.toBeNull()
const timedOutSelection = requestBrowserWebAuthnAccount(accountDetails(), browserSession)
await vi.advanceTimersByTimeAsync(BROWSER_WEBAUTHN_ACCOUNT_PICKER_TIMEOUT_MS)
await expect(timedOutSelection).resolves.toBeNull()
})
it('cancels when the guest or window is destroyed', async () => {
const guest = mockWebContents(71)
const renderer = mockWebContents(72)
fromFrameMock.mockReturnValue(guest)
getRendererContextForGuestMock.mockReturnValue({ browserPageId: 'page-4', renderer })
const browserSession = mockSession()
const guestDestroyed = requestBrowserWebAuthnAccount(accountDetails(), browserSession)
guest.emit('destroyed')
await expect(guestDestroyed).resolves.toBeNull()
const windowDestroyed = requestBrowserWebAuthnAccount(accountDetails(), browserSession)
renderer.emit('destroyed')
await expect(windowDestroyed).resolves.toBeNull()
})
it('cancels immediately when the guest or window renderer process exits', async () => {
const guest = mockWebContents(81)
const renderer = mockWebContents(82)
const browserSession = mockSession()
fromFrameMock.mockReturnValue(guest)
getRendererContextForGuestMock.mockReturnValue({ browserPageId: 'page-5', renderer })
const guestCrashed = requestBrowserWebAuthnAccount(accountDetails(), browserSession)
guest.emit('render-process-gone')
await expect(guestCrashed).resolves.toBeNull()
const windowCrashed = requestBrowserWebAuthnAccount(accountDetails(), browserSession)
renderer.emit('render-process-gone')
await expect(windowCrashed).resolves.toBeNull()
})
it.each(['guest', 'renderer'] as const)(
'retires the request when the %s commits a navigation',
async (target) => {
const owners = { guest: mockWebContents(91), renderer: mockWebContents(92) }
fromFrameMock.mockReturnValue(owners.guest)
getRendererContextForGuestMock.mockReturnValue({
browserPageId: 'page-6',
renderer: owners.renderer
})
const selection = requestBrowserWebAuthnAccount(accountDetails(), mockSession())
const requestId = vi.mocked(owners.renderer.send).mock.calls[0][1].requestId
owners[target].emit('did-navigate')
await expect(selection).resolves.toBeNull()
expect(owners.renderer.send).toHaveBeenLastCalledWith(
'browser:webauthn-account-request-closed',
{ requestId }
)
expect(
respondToBrowserWebAuthnAccountRequest(owners.renderer, {
requestId,
credentialId: 'credential-1'
})
).toBe(false)
for (const contents of Object.values(owners)) {
for (const event of ['did-navigate', 'render-process-gone', 'destroyed']) {
expect(contents.listenerCount(event)).toBe(0)
}
}
expect(vi.getTimerCount()).toBe(0)
}
)
it('keeps sibling and successor requests independent of a retired document', async () => {
const retiredGuest = mockWebContents(101)
const siblingGuest = mockWebContents(102)