mirror of
https://github.com/stablyai/orca.git
synced 2026-10-23 08:03:39 +00:00
* fix(browser): stop impersonating the source browser's UA on cookie import (STA-3514) Delete the import-time UA synthesis entirely instead of patching its version detection. The session now always keeps the engine-derived UA the registry sets at startup (clean or native), for imported and non-imported profiles alike. Why deletion is the fix: - The synthesis read CFBundleShortVersionString, which on forks is a marketing version — Arc imports presented Chrome/1.x and sites rejected the browser as ancient (STA-3514). - Its stated purpose (keep Google-bound cookies valid) is handled by isGoogleSourceBoundCookie excluding those cookies since #736, and #12884 measured that Google rejects re-transplants regardless of UA identity. - #12608 measured the synthesized Chrome-shaped UA reaching /v3/signin/rejected while the engine UA reached account lookup. Persisted userAgent/userAgentByPartition meta is no longer read; legacy keys drop off on the next meta write. The #12811 gate existed only to catch bad synthesized values, so it leaves with the synthesis. * test(browser): catch source UA impersonation regression
230 lines
7.9 KiB
TypeScript
230 lines
7.9 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import type * as fsModule from 'node:fs'
|
|
|
|
const { sessionFromPartitionMock, dialogShowOpenDialogMock } = vi.hoisted(() => ({
|
|
sessionFromPartitionMock: vi.fn(),
|
|
dialogShowOpenDialogMock: vi.fn()
|
|
}))
|
|
|
|
vi.mock('electron', () => ({
|
|
BrowserWindow: { fromWebContents: vi.fn() },
|
|
dialog: { showOpenDialog: dialogShowOpenDialogMock },
|
|
session: { fromPartition: sessionFromPartitionMock }
|
|
}))
|
|
|
|
import { BROWSER_FAMILY_LABELS } from '../../shared/constants'
|
|
|
|
function slashPath(pathValue: string): string {
|
|
return pathValue.replaceAll('\\', '/')
|
|
}
|
|
|
|
describe('detectInstalledBrowsers — Comet', () => {
|
|
const originalPlatform = process.platform
|
|
const originalHome = process.env.HOME
|
|
|
|
beforeEach(() => {
|
|
// Why: browser-cookie-import.ts uses destructured named imports from
|
|
// 'node:fs' which are bound at module-load time. Without vi.resetModules(),
|
|
// only the first test's vi.doMock takes effect — subsequent tests see the
|
|
// cached module with the first mock still applied. resetModules must run
|
|
// BEFORE each doMock so the next import() picks up the fresh mock factory.
|
|
vi.resetModules()
|
|
Object.defineProperty(process, 'platform', { value: 'darwin' })
|
|
process.env.HOME = '/Users/test'
|
|
})
|
|
|
|
afterEach(() => {
|
|
Object.defineProperty(process, 'platform', { value: originalPlatform })
|
|
process.env.HOME = originalHome
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
it('detects Comet when its data directory and Cookies DB exist', async () => {
|
|
vi.doMock('node:fs', async () => {
|
|
const actual = await vi.importActual<typeof fsModule>('node:fs')
|
|
return {
|
|
...actual,
|
|
existsSync: (p: string) => {
|
|
const normalizedPath = slashPath(p)
|
|
if (normalizedPath.includes('Comet/Default/Network/Cookies')) {
|
|
return true
|
|
}
|
|
if (normalizedPath.includes('Comet/Local State')) {
|
|
return true
|
|
}
|
|
return false
|
|
},
|
|
readFileSync: (p: string, enc?: string) => {
|
|
if (typeof p === 'string' && slashPath(p).includes('Comet/Local State')) {
|
|
return JSON.stringify({ profile: { info_cache: { Default: { name: 'Default' } } } })
|
|
}
|
|
return actual.readFileSync(p as never, enc as never)
|
|
}
|
|
}
|
|
})
|
|
|
|
const { detectInstalledBrowsers } = await import('./browser-cookie-import')
|
|
const detected = detectInstalledBrowsers()
|
|
const comet = detected.find((b) => b.family === 'comet')
|
|
expect(comet).toBeDefined()
|
|
expect(comet?.label).toBe('Comet')
|
|
expect(slashPath(comet?.cookiesPath ?? '')).toContain('Comet/Default/Network/Cookies')
|
|
expect(comet?.keychainService).toBe('Comet Safe Storage')
|
|
})
|
|
|
|
it('does not list Comet when its data directory is absent', async () => {
|
|
vi.doMock('node:fs', async () => {
|
|
const actual = await vi.importActual<typeof fsModule>('node:fs')
|
|
return {
|
|
...actual,
|
|
existsSync: () => false
|
|
}
|
|
})
|
|
|
|
const { detectInstalledBrowsers } = await import('./browser-cookie-import')
|
|
const detected = detectInstalledBrowsers()
|
|
expect(detected.find((b) => b.family === 'comet')).toBeUndefined()
|
|
})
|
|
|
|
it('enumerates all Comet profiles from Local State info_cache', async () => {
|
|
vi.doMock('node:fs', async () => {
|
|
const actual = await vi.importActual<typeof fsModule>('node:fs')
|
|
return {
|
|
...actual,
|
|
existsSync: (p: string) => {
|
|
const normalizedPath = slashPath(p)
|
|
if (normalizedPath.includes('Comet/Default/Network/Cookies')) {
|
|
return true
|
|
}
|
|
if (normalizedPath.includes('Comet/Local State')) {
|
|
return true
|
|
}
|
|
return false
|
|
},
|
|
readFileSync: (p: string, enc?: string) => {
|
|
if (typeof p === 'string' && slashPath(p).includes('Comet/Local State')) {
|
|
return JSON.stringify({
|
|
profile: {
|
|
info_cache: {
|
|
Default: { name: 'Personal' },
|
|
'Profile 1': { name: 'Work' },
|
|
'Profile 2': { name: 'Research' }
|
|
}
|
|
}
|
|
})
|
|
}
|
|
return actual.readFileSync(p as never, enc as never)
|
|
}
|
|
}
|
|
})
|
|
|
|
const { detectInstalledBrowsers } = await import('./browser-cookie-import')
|
|
const detected = detectInstalledBrowsers()
|
|
const comet = detected.find((b) => b.family === 'comet')
|
|
expect(comet).toBeDefined()
|
|
const directories = comet!.profiles.map((p) => p.directory).sort()
|
|
expect(directories).toEqual(['Default', 'Profile 1', 'Profile 2'])
|
|
const names = comet!.profiles.map((p) => p.name).sort()
|
|
expect(names).toEqual(['Personal', 'Research', 'Work'])
|
|
})
|
|
|
|
it('ignores Comet profile directories that escape the browser root', async () => {
|
|
vi.doMock('node:fs', async () => {
|
|
const actual = await vi.importActual<typeof fsModule>('node:fs')
|
|
return {
|
|
...actual,
|
|
existsSync: (p: string) => {
|
|
if (p.includes('Comet/Local State')) {
|
|
return true
|
|
}
|
|
if (p.includes('Application Support/Outside/Network/Cookies')) {
|
|
return true
|
|
}
|
|
return false
|
|
},
|
|
readFileSync: (p: string, enc?: string) => {
|
|
if (typeof p === 'string' && p.includes('Comet/Local State')) {
|
|
return JSON.stringify({
|
|
profile: {
|
|
info_cache: {
|
|
'../Outside': { name: 'Outside' }
|
|
}
|
|
}
|
|
})
|
|
}
|
|
return actual.readFileSync(p as never, enc as never)
|
|
}
|
|
}
|
|
})
|
|
|
|
const { detectInstalledBrowsers } = await import('./browser-cookie-import')
|
|
const detected = detectInstalledBrowsers()
|
|
expect(detected.find((b) => b.family === 'comet')).toBeUndefined()
|
|
})
|
|
|
|
it('rejects explicit Comet profile selections that escape the browser root', async () => {
|
|
vi.doMock('node:fs', async () => {
|
|
const actual = await vi.importActual<typeof fsModule>('node:fs')
|
|
return {
|
|
...actual,
|
|
existsSync: (p: string) => {
|
|
if (p.includes('Application Support/Outside/Network/Cookies')) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
})
|
|
|
|
const { selectBrowserProfile } = await import('./browser-cookie-import')
|
|
const selected = selectBrowserProfile(
|
|
{
|
|
family: 'comet',
|
|
label: 'Comet',
|
|
cookiesPath: '/Users/test/Library/Application Support/Comet/Default/Network/Cookies',
|
|
keychainService: 'Comet Safe Storage',
|
|
keychainAccount: 'Comet',
|
|
profiles: [{ name: 'Outside', directory: '../Outside' }],
|
|
selectedProfile: 'Default'
|
|
},
|
|
'../Outside'
|
|
)
|
|
|
|
expect(selected).toBeNull()
|
|
})
|
|
|
|
it('skips Comet when the data directory exists but no Cookies DB is present', async () => {
|
|
vi.doMock('node:fs', async () => {
|
|
const actual = await vi.importActual<typeof fsModule>('node:fs')
|
|
return {
|
|
...actual,
|
|
existsSync: (p: string) => {
|
|
if (p.includes('Comet/Local State')) {
|
|
return true
|
|
}
|
|
if (p.includes('Network/Cookies') || p.endsWith('/Cookies')) {
|
|
return false
|
|
}
|
|
return false
|
|
},
|
|
readFileSync: (p: string, enc?: string) => {
|
|
if (typeof p === 'string' && p.includes('Comet/Local State')) {
|
|
return JSON.stringify({ profile: { info_cache: { Default: { name: 'Default' } } } })
|
|
}
|
|
return actual.readFileSync(p as never, enc as never)
|
|
}
|
|
}
|
|
})
|
|
|
|
const { detectInstalledBrowsers } = await import('./browser-cookie-import')
|
|
const detected = detectInstalledBrowsers()
|
|
expect(detected.find((b) => b.family === 'comet')).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('BROWSER_FAMILY_LABELS — Comet', () => {
|
|
it('maps the comet family key to the user-facing label "Comet"', () => {
|
|
expect(BROWSER_FAMILY_LABELS.comet).toBe('Comet')
|
|
})
|
|
})
|