Skip to content

Commit 7d13c59

Browse files
hi-ogawaOpenCode (claude-opus-4-8)
andauthored
fix: check fs access in builtin commands (#1)
Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Co-authored-by: OpenCode (claude-opus-4-8) <noreply@opencode.ai>
1 parent d4c6750 commit 7d13c59

3 files changed

Lines changed: 40 additions & 0 deletions

File tree

src/commands/permissions.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { TestProject } from 'vitest/node'
2+
import { isFileLoadingAllowed } from 'vitest/node'
3+
4+
const BACKSLASH_RE = /\\/g
5+
const REPEATED_SLASH_RE = /\/+/g
6+
7+
function slash(path: string): string {
8+
return path.replace(BACKSLASH_RE, '/').replace(REPEATED_SLASH_RE, '/')
9+
}
10+
11+
export function assertBrowserFileAccess(project: TestProject, path: string): void {
12+
const normalized = slash(path)
13+
if (
14+
!isFileLoadingAllowed(project.vite.config, normalized)
15+
&& !isFileLoadingAllowed(project.vitest.vite.config, normalized)
16+
) {
17+
throw new Error(
18+
`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`,
19+
)
20+
}
21+
}
22+
23+
export function assertBrowserApiWrite(project: TestProject, path: string): void {
24+
// `browser.api` is unified into the main `api` config since
25+
// https://github.com/vitest-dev/vitest/pull/10554
26+
const browserApiAllowWrite = project.config.browser.api
27+
? project.config.browser.api.allowWrite
28+
: project.config.api.allowWrite
29+
if (!browserApiAllowWrite || !project.vitest.config.api.allowWrite) {
30+
throw new Error(
31+
`Cannot modify file "${path}". File writing is disabled because the server is exposed to the internet, see https://vitest.dev/config/browser/api.`,
32+
)
33+
}
34+
}

src/commands/screenshot.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { mkdir, rm } from 'node:fs/promises'
66
import { normalize as platformNormalize } from 'node:path'
77
import { resolveScreenshotPath } from '@vitest/browser'
88
import { dirname, normalize, resolve } from 'pathe'
9+
import { assertBrowserApiWrite, assertBrowserFileAccess } from './permissions'
910

1011
interface ScreenshotCommandOptions extends Omit<ScreenshotOptions, 'element' | 'mask'> {
1112
element?: SerializedLocator
@@ -43,6 +44,9 @@ export async function takeScreenshot(
4344
if (options.save) {
4445
savePath = normalize(path)
4546

47+
assertBrowserApiWrite(context.project, savePath)
48+
assertBrowserFileAccess(context.project, savePath)
49+
4650
await mkdir(dirname(savePath), { recursive: true })
4751
}
4852

src/commands/upload.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { SerializedLocator } from '@vitest/browser'
22
import type { UserEventUploadOptions } from 'vitest/browser'
33
import type { UserEventCommand } from './utils'
44
import { resolve } from 'pathe'
5+
import { assertBrowserFileAccess } from './permissions'
56

67
export const upload: UserEventCommand<(element: SerializedLocator, files: Array<string | {
78
name: string
@@ -29,6 +30,7 @@ export const upload: UserEventCommand<(element: SerializedLocator, files: Array<
2930

3031
for (const file of files) {
3132
const filepath = resolve(root, file as string)
33+
assertBrowserFileAccess(context.project, filepath)
3234
const remoteFilePath = await context.browser.uploadFile(filepath)
3335
await element.addValue(remoteFilePath)
3436
}

0 commit comments

Comments
 (0)