{"id":"GHSA-p63j-vcc4-9vmv","summary":"@vitest/browser: Browser Mode provider commands bypass the file-access permission gate","details":"## Summary\n\nBrowser Mode exposes a set of built-in \"commands\" that run on the Node.js side of the test runner and can touch the local filesystem (taking screenshots, managing Playwright traces, uploading files for `\u003cinput type=\"file\"\u003e`, comparing screenshots).\n\nSeveral of these commands accept a file path from the browser and act on it without checking the `allowWrite` permission gate and without confining the path to the project directory. A client that can reach the Browser Mode API can therefore read, create, overwrite, or delete files anywhere the Vitest process can access, even when `allowWrite` is `false`.\n\nThis matters most when the Browser Mode API is exposed to the network (for example `test.api.host` is set, or the dev server is reachable from another machine or origin). In that configuration `allowWrite` defaults to `false` precisely to block file access, and these commands bypass that protection. On a default localhost-only setup with trusted test code, there is no untrusted party in a position to exploit it. The gap still matters wherever you rely on `allowWrite: false` to contain untrusted test code, because these commands ignore that flag.\n\n## Affected commands and impact\n\n| Command | Operation | Impact |\n|---|---|---|\n| `upload` (Playwright + WebdriverIO) | Read | Arbitrary local file read; contents are loaded into the page and readable by test code. Highest-impact case. |\n| `takeScreenshot` (Playwright + WebdriverIO) | Write | Writes a PNG to an arbitrary path (absolute path used verbatim), creating parent directories. |\n| `screenshotMatcher` | Write | Writes reference/diff PNGs; directory derived from client path allows partial traversal. |\n| `stopChunkTrace` | Write | Writes a Playwright trace `.zip` to a path escapable via `../` in the trace name. |\n| `deleteTracing` | Delete | Deletes arbitrary files by path. |\n| `annotateTraces` | Read (disclosure) | Records a client-controlled attachment path that the reporter copies into the attachments directory, disclosing file contents. |\n\nThe writes do not let an attacker choose the file contents (they produce PNG images or trace archives), so the integrity impact is creating, overwriting, or deleting a file at an arbitrary path rather than writing a chosen payload. The reads (`upload`, `annotateTraces`) are more serious because they expose the full contents of an arbitrary file.\n\nThe fix adds, to every file-touching provider command, an `allowWrite` check for write/delete operations and path confinement to the project root (matching the existing `fs` command pattern), so client-supplied absolute paths and `../` traversal are rejected.","aliases":["CVE-2026-73653"],"modified":"2026-09-10T03:51:11.555752293Z","published":"2026-07-21T19:36:12Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-07-21T19:36:12Z","nvd_published_at":null,"cwe_ids":["CWE-22","CWE-552","CWE-862"],"severity":"CRITICAL"},"references":[{"type":"WEB","url":"https://github.com/vitest-dev/vitest/security/advisories/GHSA-p63j-vcc4-9vmv"},{"type":"WEB","url":"https://github.com/vitest-dev/vitest/pull/10674"},{"type":"WEB","url":"https://github.com/vitest-dev/vitest/pull/10679"},{"type":"WEB","url":"https://github.com/vitest-dev/vitest/pull/10680"},{"type":"WEB","url":"https://github.com/vitest-dev/vitest/commit/33f96a145ef09ca6a43b4e555eb273e64a87be23"},{"type":"WEB","url":"https://github.com/vitest-dev/vitest/commit/5c18dd267ff7f47f24cab2f615a16b37d90feb7f"},{"type":"WEB","url":"https://github.com/vitest-dev/vitest/commit/b795e36b34969bec50b47a9f29d26f799a6a04fb"},{"type":"PACKAGE","url":"https://github.com/vitest-dev/vitest"},{"type":"WEB","url":"https://github.com/vitest-dev/vitest/releases/tag/v3.2.7"},{"type":"WEB","url":"https://github.com/vitest-dev/vitest/releases/tag/v4.1.10"},{"type":"WEB","url":"https://github.com/vitest-dev/vitest/releases/tag/v5.0.0-beta.6"}],"affected":[{"package":{"name":"@vitest/browser","ecosystem":"npm","purl":"pkg:npm/%40vitest/browser"},"ranges":[{"type":"SEMVER","events":[{"introduced":"4.0.0"},{"fixed":"4.1.10"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-p63j-vcc4-9vmv/GHSA-p63j-vcc4-9vmv.json"}},{"package":{"name":"@vitest/browser","ecosystem":"npm","purl":"pkg:npm/%40vitest/browser"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.2.7"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-p63j-vcc4-9vmv/GHSA-p63j-vcc4-9vmv.json"}},{"package":{"name":"@vitest/browser","ecosystem":"npm","purl":"pkg:npm/%40vitest/browser"},"ranges":[{"type":"SEMVER","events":[{"introduced":"5.0.0-beta.1"},{"fixed":"5.0.0-beta.6"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-p63j-vcc4-9vmv/GHSA-p63j-vcc4-9vmv.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L"}]}