mirror of
https://github.com/stablyai/orca.git
synced 2026-10-14 16:02:57 +00:00
fix: validate chromium profile directories
This commit is contained in:
@@ -122,6 +122,40 @@ describe('detectInstalledBrowsers — Comet', () => {
|
||||
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('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')
|
||||
|
||||
@@ -173,6 +173,17 @@ function resolveCookiesPath(profileDir: string): string | null {
|
||||
return null
|
||||
}
|
||||
|
||||
function isSafeBrowserProfileDirectory(directory: string): boolean {
|
||||
return (
|
||||
directory.length > 0 &&
|
||||
directory !== '.' &&
|
||||
!directory.includes('\0') &&
|
||||
!directory.includes('/') &&
|
||||
!directory.includes('\\') &&
|
||||
!directory.includes('..')
|
||||
)
|
||||
}
|
||||
|
||||
// Why: Chrome's Local State JSON contains profile.info_cache which maps profile
|
||||
// directory names (e.g. "Default", "Profile 1") to metadata including the
|
||||
// user-visible display name. This lets us show human-readable names in the picker.
|
||||
@@ -190,6 +201,10 @@ function discoverProfiles(browserRoot: string): BrowserProfile[] {
|
||||
}
|
||||
const profiles: BrowserProfile[] = []
|
||||
for (const [dir, info] of Object.entries(infoCache)) {
|
||||
// Why: Local State is external metadata, but profile dirs become path segments.
|
||||
if (!isSafeBrowserProfileDirectory(dir)) {
|
||||
continue
|
||||
}
|
||||
const profileName = (info as { name?: string })?.name ?? dir
|
||||
profiles.push({ name: profileName, directory: dir })
|
||||
}
|
||||
@@ -362,6 +377,9 @@ export function selectBrowserProfile(
|
||||
browser: DetectedBrowser,
|
||||
profileDirectory: string
|
||||
): DetectedBrowser | null {
|
||||
if (!isSafeBrowserProfileDirectory(profileDirectory)) {
|
||||
return null
|
||||
}
|
||||
if (browser.family === 'firefox') {
|
||||
const profilesRoot = firefoxProfilesRoot()
|
||||
if (!profilesRoot) {
|
||||
|
||||
Reference in New Issue
Block a user