diff --git a/src/main/browser/browser-session-registry.persistence.test.ts b/src/main/browser/browser-session-registry.persistence.test.ts index 697e9448d..346d08132 100644 --- a/src/main/browser/browser-session-registry.persistence.test.ts +++ b/src/main/browser/browser-session-registry.persistence.test.ts @@ -243,4 +243,37 @@ describe('BrowserSessionRegistry persistence', () => { expect(written.pendingCookieImports).toEqual({ [importedPartition]: '/staged/imported' }) 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) + }) }) diff --git a/src/main/browser/browser-session-registry.ts b/src/main/browser/browser-session-registry.ts index 43bc0fb67..857c5dc3c 100644 --- a/src/main/browser/browser-session-registry.ts +++ b/src/main/browser/browser-session-registry.ts @@ -192,10 +192,14 @@ class BrowserSessionRegistry { if (pendingEntries.length === 0) { return } - const knownPartitions = new Set([ - ORCA_BROWSER_PARTITION, - ...meta.profiles.map((p) => p.partition) - ]) + // Why: replay writes to partition-derived file paths, so corrupted + // metadata must pass the same validation as the webview allowlist. + const knownPartitions = new Set([ORCA_BROWSER_PARTITION]) + for (const profile of meta.profiles) { + if (BrowserSessionRegistry.isValidPersistedProfile(profile)) { + knownPartitions.add(profile.partition) + } + } const remainingEntries = { ...meta.pendingCookieImports } for (const [partition, stagedPath] of pendingEntries) { @@ -426,17 +430,24 @@ class BrowserSessionRegistry { // registering anything. private static readonly PARTITION_RE = /^persist:orca-browser-session-[\da-f-]{36}$/ + private static isValidPersistedProfile(profile: unknown): profile is BrowserSessionProfile { + if (!profile || typeof profile !== 'object') { + return false + } + const candidate = profile as Partial + return ( + candidate.id !== 'default' && + candidate.scope !== 'default' && + typeof candidate.id === 'string' && + typeof candidate.partition === 'string' && + typeof candidate.label === 'string' && + BrowserSessionRegistry.PARTITION_RE.test(candidate.partition) + ) + } + hydrateFromPersisted(profiles: BrowserSessionProfile[]): void { for (const profile of profiles) { - if (profile.id === 'default' || profile.scope === 'default') { - continue - } - if ( - typeof profile.id !== 'string' || - typeof profile.partition !== 'string' || - typeof profile.label !== 'string' || - !BrowserSessionRegistry.PARTITION_RE.test(profile.partition) - ) { + if (!BrowserSessionRegistry.isValidPersistedProfile(profile)) { continue } this.profiles.set(profile.id, profile)