fix(host,client): require fully qualified browse paths; clear the picker kind on close
ds-review-bot round 4. On Windows, isAbsolute admits rooted drive-less forms (\foo, /foo) that resolve() then rebases onto the process's current drive; both browse primitives now gate on a fullyQualified check (drive letter or UNC on win32, POSIX-absolute elsewhere) with a platform test seam, per-platform unit cases, and the contract wording updated on the seam, the backend README pair, and the error messages. The picker-kind effect also kept a resolved 'dialog' across close, so a backend swapped while the menu was closed could paint the stale entry for one frame on reopen; the close arm now clears the state, pinned by a reopen-under-pending-read race test.
This commit is contained in:
@@ -7,7 +7,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { DirectoryPickerError } from '@deepseek-ai/dsh-host-directory-picker'
|
||||
import type { DirectoryPickerBrowseCapability } from '@deepseek-ai/dsh-host-directory-picker'
|
||||
import BrowseDirectoryPicker from '../src/index.ts'
|
||||
import BrowseDirectoryPicker, { fullyQualified } from '../src/index.ts'
|
||||
|
||||
let root: string
|
||||
let capability: DirectoryPickerBrowseCapability
|
||||
@@ -70,6 +70,19 @@ describe('BrowseDirectoryPicker', () => {
|
||||
expect((failure as DirectoryPickerError).path).toBe(missing)
|
||||
})
|
||||
|
||||
it('classifies fully qualified paths per platform (drive-less rooted Windows forms rejected)', () => {
|
||||
expect(fullyQualified('/home/x', 'linux')).toBe(true)
|
||||
expect(fullyQualified('x/y', 'darwin')).toBe(false)
|
||||
expect(fullyQualified('C:\\projects', 'win32')).toBe(true)
|
||||
expect(fullyQualified('C:/projects', 'win32')).toBe(true)
|
||||
expect(fullyQualified('\\\\server\\share', 'win32')).toBe(true)
|
||||
// Rooted but drive-less: isAbsolute accepts these, yet resolve() would
|
||||
// inject the process's current drive.
|
||||
expect(fullyQualified('\\foo', 'win32')).toBe(false)
|
||||
expect(fullyQualified('/foo', 'win32')).toBe(false)
|
||||
expect(fullyQualified('C:relative', 'win32')).toBe(false)
|
||||
})
|
||||
|
||||
it('rejects non-absolute paths instead of rebasing them under the process cwd', async () => {
|
||||
for (const relative of ['', 'projects', './projects', '..']) {
|
||||
const listFailure = await capability.list(relative).catch((error: unknown) => error)
|
||||
|
||||
Reference in New Issue
Block a user