|
| 1 | +import type { DevToolsNodeContext } from '@vitejs/devtools-kit' |
| 2 | +import { resolve } from 'node:path' |
| 3 | +import { describe, expect, it, vi } from 'vitest' |
| 4 | +import { openInEditor } from '../rpc/public/open-in-editor' |
| 5 | + |
| 6 | +// Mock launch-editor so tests don't actually open files |
| 7 | +vi.mock('launch-editor', () => ({ |
| 8 | + default: vi.fn(), |
| 9 | +})) |
| 10 | + |
| 11 | +describe('openInEditor – path traversal protection', () => { |
| 12 | + const cwd = resolve('/project/root') |
| 13 | + const workspaceRoot = resolve('/project') |
| 14 | + const mockContext = { cwd, workspaceRoot } as DevToolsNodeContext |
| 15 | + |
| 16 | + async function getHandler() { |
| 17 | + const setup = openInEditor.setup! |
| 18 | + const { handler } = await setup(mockContext) |
| 19 | + expect(handler).toBeTypeOf('function') |
| 20 | + return handler as (path: string) => Promise<void> |
| 21 | + } |
| 22 | + |
| 23 | + it('allows opening a file inside the project root', async () => { |
| 24 | + const handler = await getHandler() |
| 25 | + await expect(handler('src/main.ts')).resolves.not.toThrow() |
| 26 | + }) |
| 27 | + |
| 28 | + it('allows opening a nested file inside the project root', async () => { |
| 29 | + const handler = await getHandler() |
| 30 | + await expect(handler('src/utils/helper.ts')).resolves.not.toThrow() |
| 31 | + }) |
| 32 | + |
| 33 | + it('rejects path traversal with ../', async () => { |
| 34 | + const handler = await getHandler() |
| 35 | + await expect(handler('../../etc/passwd')).rejects.toThrow( |
| 36 | + 'Path is outside the workspace root', |
| 37 | + ) |
| 38 | + }) |
| 39 | + |
| 40 | + it('rejects absolute path outside project root', async () => { |
| 41 | + const handler = await getHandler() |
| 42 | + await expect(handler('/etc/passwd')).rejects.toThrow( |
| 43 | + 'Path is outside the workspace root', |
| 44 | + ) |
| 45 | + }) |
| 46 | + |
| 47 | + it('rejects traversal disguised within a subpath', async () => { |
| 48 | + const handler = await getHandler() |
| 49 | + await expect(handler('src/../../secret/file.txt')).rejects.toThrow( |
| 50 | + 'Path is outside the workspace root', |
| 51 | + ) |
| 52 | + }) |
| 53 | +}) |
0 commit comments