diff --git a/docs/api/browser/commands.md b/docs/api/browser/commands.md index 6306ae6e4..73f782c5b 100644 --- a/docs/api/browser/commands.md +++ b/docs/api/browser/commands.md @@ -16,9 +16,9 @@ You can use the `readFile`, `writeFile`, and `removeFile` APIs to handle files i By default, Vitest uses `utf-8` encoding but you can override it with options. ::: tip -This API follows [`server.fs`](https://vitejs.dev/config/server-options.html#server-fs-allow) limitations for security reasons. +The built-in file commands follow Vite's [`server.fs`](https://vitejs.dev/config/server-options.html#server-fs-allow) restrictions for security reasons. -If [`browser.api.allowWrite`](/config/browser/api) or [`api.allowWrite`](/config/api#api-allowwrite) are disabled, `writeFile` and `removeFile` functions won't do anything. +`writeFile` and `removeFile` also require write access through [`browser.api.allowWrite`](/config/browser/api) and [`api.allowWrite`](/config/api#api-allowwrite). ::: ```ts @@ -124,6 +124,54 @@ declare module 'vitest/browser' { Custom functions will override built-in ones if they have the same name. ::: +::: warning Security +Custom commands run in the Vitest Node process and are callable from browser test code through Vitest's browser RPC connection. They can access local files, environment variables, network services, databases, shell commands, and other Node APIs. + +Vitest's built-in file commands validate paths against Vite's [`server.fs`](https://vite.dev/config/server-options#server-fs-allow) restrictions and separately check whether writes are allowed. Custom commands do not automatically inherit these protections. If a custom command accepts browser-provided input and uses it to read, write, delete, execute, or expose local resources, validate that input before using it. + +For file reads or fixture loading, use `isFileLoadingAllowed` from `vitest/node` or an explicit allowlist. For writes and deletes, also require an explicit mutation policy, such as [`browser.api.allowWrite`](/config/browser/api#api-allowwrite), [`api.allowWrite`](/config/api#api-allowwrite), and a command-specific allowed directory. For commands that execute code, shell commands, or project scripts, also check [`browser.api.allowExec`](/config/browser/api#api-allowexec) and [`api.allowExec`](/config/api#api-allowexec). + +For example, if you create your own file-writing command instead of using Vitest's built-in `writeFile`, apply the same checks: + +```ts +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { normalizePath } from 'vite' +import { isFileLoadingAllowed } from 'vitest/node' +import type { BrowserCommand } from 'vitest/node' + +function assertFileAccess(path: string, project: any) { + if ( + !isFileLoadingAllowed(project.vite.config, path) + && !isFileLoadingAllowed(project.vitest.vite.config, path) + ) { + throw new Error(`Access denied to "${path}".`) + } +} + +function assertWrite(project: any) { + if (!project.config.browser.api.allowWrite || !project.vitest.config.api.allowWrite) { + throw new Error('Writing files is disabled.') + } +} + +export const myWriteFileCommand: BrowserCommand<[path: string, content: string]> = async ( + { project }, + path, + content, +) => { + assertWrite(project) + + const file = resolve(project.config.root, path) + assertFileAccess(normalizePath(file), project) + + await mkdir(dirname(file), { recursive: true }) + await writeFile(file, content) +} +``` + +::: + ### Recording trace markers Custom commands can record [trace markers](/api/browser/context#mark) for the test that triggered them through `context.mark`. This is the server-side equivalent of `page.mark` and helps annotate the [trace view](/guide/browser/trace-view) with custom actions performed inside a command. diff --git a/docs/config/browser/commands.md b/docs/config/browser/commands.md index b933f093a..8d53c636a 100644 --- a/docs/config/browser/commands.md +++ b/docs/config/browser/commands.md @@ -9,3 +9,9 @@ outline: deep - **Default:** `{ readFile, writeFile, ... }` Custom [commands](/api/browser/commands) that can be imported during browser tests from `vitest/browser`. + +::: warning Security +Commands run in the Vitest Node process. If a command exposes filesystem, process, network, database, or shell access based on browser-provided input, validate and restrict that input inside the command. Built-in file commands apply Vite `server.fs` checks and write-access checks, but custom commands are responsible for their own protections. + +See [Custom Commands security notes](/api/browser/commands#custom-commands). +:::