CVE-2026-73653 Vitest Browser Mode 深度拆解:provider commands 绕过 allowWrite 的文件读写漏洞
导语
2026 年 9 月 9 日,GitHub Security Advisory 发布了一个 CVSS 9.4 分的 Critical 级漏洞:CVE-2026-73653(GHSA-p63j-vcc4-9vmv),影响 Vitest 的浏览器模式组件 @vitest/browser。
漏洞核心:Vitest Browser Mode 的 6 个内置 provider commands(如 upload、takeScreenshot、screenshotMatcher、stopChunkTrace、deleteTracing、annotateTraces)未正确检查 allowWrite 权限门,攻击者通过恶意测试用例中的路径穿越操作,可以绕过 allowWrite 限制,在目标机器上实现任意文件读取、写入和删除——直接威胁到使用 Vitest Browser Mode 的 CI/CD 流水线和开发者的本机环境。
本文从 Vitest Browser Mode 架构、provider commands 权限模型、漏洞根因、攻击链构建到完整修复时间线,做系统性深度拆解。
背景:Vitest Browser Mode 是什么
Vitest 与浏览器测试
Vitest 是 2024-2026 年最流行的 JavaScript/TypeScript 测试框架之一,凭借与 Vite 的深度集成、兼容 Jest API 以及极速执行能力,成为 React、Vue、Svelte 等现代前端框架的默认测试工具。
Vitest Browser Mode(@vitest/browser)是 Vitest 的浏览器内测试组件。它允许开发者在真实浏览器环境中运行测试,覆盖 DOM 交互、CSS 渲染、网络请求等仅在浏览器中可复现的场景——远超 jsdom/happy-dom 等模拟环境的能力。
传统 Vitest 单元测试 Vitest Browser Mode
┌──────────────┐ ┌──────────────┐
│ Node.js │ │ 浏览器环境 │
│ happy-dom │ ← 模拟 DOM │ Chrome/Edge │ ← 真实 DOM
│ 无网络请求 │ │ 真实网络栈 │
│ 无 CSS 渲染 │ │ CSS 渲染 │
└──────────────┘ └──────────────┘
│ │
└── 单元测试覆盖率高 └── 集成测试覆盖率高
但无法测试真实浏览器行为 但暴露真实浏览器攻击面Provider Commands 架构
Browser Mode 的核心设计采用 Provider Architecture:
- 测试在浏览器端执行(页面上下文)
- 通过 Provider Commands 与 Node.js 主进程通信
- Provider Commands 是内部 RPC 机制,允许浏览器端触发 Node.js 端操作(如文件读写、截图、追踪)
┌─────────────────────────────┐ ┌──────────────────────────────┐
│ 浏览器(测试运行上下文) │ │ Node.js Provider 进程 │
│ │ │ │
│ test.run() │ │ provider.execute() │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ provider commands 调用 │──────→│ 检查 allowWrite → 执行操作 │
│ (upload/screenshot/...) │ RPC │ (写入文件/截图/删除追踪) │
└─────────────────────────────┘ └──────────────────────────────┘这 6 个内置 provider commands 本质上是 Node.js 侧的特权操作——它们拥有文件系统访问权限,理论上应在 allowWrite 白名单控制下运行。
漏洞根因:权限检查缺失
CVE-2026-73653 的技术本质
漏洞报告者(manus-use,2026-07-08)发现,@vitest/browser 的 provider commands 存在 系统性权限检查遗漏:
6 个内置 provider commands 中,部分命令未在执行前检查调用者是否拥有 allowWrite 权限。
| Provider Command | 功能描述 | 实际权限 | 漏洞行为 |
|---|---|---|---|
upload | 上传文件到浏览器端 | 读文件 | 可读取目标机器任意文件 |
takeScreenshot | 截取当前页面截图 | 写文件 | 可写入目标机器任意路径 |
screenshotMatcher | 截取匹配元素截图 | 写文件 | 可写入目标机器任意路径 |
stopChunkTrace | 停止追踪分块 | 写文件 | 可写入目标机器任意路径 |
deleteTracing | 删除追踪文件 | 删文件 | 可删除目标机器任意文件 |
annotateTraces | 标注追踪信息 | 读文件 | 可读取追踪目录任意文件(信息泄露) |
漏洞根因:Vitest 的权限模型假设 provider commands 只会被可信的测试代码调用。但当测试用例中包含恶意代码(例如开源依赖被供应链投毒、CI/CD 中运行不受信任的 PR 测试),攻击者可以直接构造特殊路径,让 provider commands 越过 allowWrite 白名单执行任意文件操作。
CWE 分类
该漏洞涉及三个 CWE:
- CWE-22: Path Traversal(路径穿越)——攻击者通过
../或绝对路径穿越allowWrite限制的目录范围 - CWE-552: Files or Directories Accessible to External Parties(权限配置不当)——provider commands 向不应访问的外部路径开放了文件操作权限
- CWE-862: Missing Authorization(授权缺失)——未检查调用者是否拥有执行特定操作的权限
攻击面分析
攻击向量取决于测试用例的来源:
场景一:恶意 PR 测试(供应链攻击)
攻击者提 PR → CI/CD 自动触发 vitest --browser
→ 恶意测试用例中调用 provider command
→ provider 未检查 allowWrite → 写入 SSH key / 环境变量到公开目录
→ 攻击者读取泄露的凭证场景二:恶意 npm 包(测试框架生态投毒)
开发者安装被投毒的测试工具库(如 vitest-plugin-xxx)
→ 插件在 vitest.config 中注册自定义 provider
→ 测试运行时恶意 provider 触发文件操作
→ 由于 provider command 层面无权限检查,直接越权场景三:本地开发环境
开发者在本地执行 git clone 并运行测试
→ 恶意 vitest.config 引导执行 malicious test
→ provider commands 写入 ~/.ssh/id_rsa 到 /tmp/.leaked
→ 开发者机器被入侵影响范围与修复
受影响版本
| 包名 | 受影响版本 | 修复版本 |
|---|---|---|
@vitest/browser | ≤ 4.1.9 | 4.1.10 |
@vitest/browser | ≤ 3.2.6 | 3.2.7 |
@vitest/browser | ≤ 5.0.0-beta.5 | 5.0.0-beta.6 |
修复方案
Vitest 团队在修复版本中为所有 6 个内置 provider commands 增加了 权限前置检查:在执行任何文件系统操作前,验证当前请求路径是否落在 allowWrite 白名单目录内。未通过检查的请求将被拒绝并抛出权限错误。
立即升级:
# npm
npm install @vitest/browser@latest
# yarn
yarn add @vitest/browser@latest
# pnpm
pnpm add @vitest/browser@latest短期缓解(无法立即升级时)
在 vitest.config 中显式禁用不需要的 provider commands,或通过自定义 provider 封装,添加路径检查中间件:
// vitest.config.ts - 短期缓解方案
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
browser: {
// 仅允许特定目录的写操作
allowWrite: ['/tmp/vitest-screenshots/', '/tmp/vitest-traces/'],
},
},
})与同类型漏洞的对比
CVE-2026-73653 本质上是权限模型完整性漏洞(Privilege Model Integrity),与以下漏洞属于同类:
| 漏洞 | 本质 | 后果 |
|---|---|---|
| CVE-2026-73653(Vitest) | Provider commands 未检查 allowWrite | 测试工具链任意文件读写 |
| CVE-2026-50548(Cursor) | AI Agent 工具链路径穿越 | IDE 沙箱逃逸 |
| CVE-2026-48710(Starlette/FastAPI) | Host header 路径注入 | 认证绕过 |
| CVE-2026-61539(Xinference) | eval() 注入 → RCE | AI 平台接管 |
共同特征:应用层授权逻辑在底层系统调用时未做校验——应用假设调用者可信,但系统层面缺乏最小权限原则。
编码层面的修复原理
修复前代码(概念还原)
// provider commands 执行入口(简化)
async function executeProviderCommand(command, params) {
// ❌ 修复前:未检查 params.path 是否在 allowWrite 白名单内
switch (command) {
case 'upload':
return await fs.readFile(params.path) // 任意读
case 'takeScreenshot':
case 'screenshotMatcher':
case 'stopChunkTrace':
return await fs.writeFile(params.path, params.data) // 任意写
case 'deleteTracing':
return await fs.unlink(params.path) // 任意删
case 'annotateTraces':
return await fs.readFile(params.path) // 任意读
}
}修复后代码(概念还原)
// 修复后的权限检查
function isPathAllowed(targetPath: string, allowedPaths: string[]): boolean {
const resolved = path.resolve(targetPath)
return allowedPaths.some(
allowed => resolved.startsWith(path.resolve(allowed))
)
}
async function executeProviderCommand(command, params, allowWrite) {
const needsWrite = ['takeScreenshot', 'screenshotMatcher', 'stopChunkTrace']
const needsRead = ['upload', 'annotateTraces']
const needsDelete = ['deleteTracing']
if (needsWrite.includes(command) || needsDelete.includes(command)) {
if (!isPathAllowed(params.path, allowWrite)) {
throw new Error(`Permission denied: ${params.path} is not in allowWrite`)
}
}
if (needsRead.includes(command)) {
if (!isPathAllowed(params.path, allowWrite)) {
throw new Error(`Permission denied: ${params.path} is not in allowWrite`)
}
}
// ... 执行实际操作
}Timeline
| 日期 | 事件 |
|---|---|
| 2026-07-08 | 漏洞报告者(manus-use)提交安全报告至 Vitest 团队 |
| 2026-07-08 | GitHub Security Advisory(GHSA-p63j-vcc4-9vmv)发布 |
| 2026-09-09 | 漏洞详情公开(TheHackerWire 报道) |
| 修复版本 | 4.1.10 / 3.2.7 / 5.0.0-beta.6 |
从报告到公开披露约 63 天,属于负责任披露流程。
防御建议
- 立即升级
@vitest/browser至修复版本(4.1.10 / 3.2.7 / 5.0.0-beta.6) - CI/CD 隔离:在隔离容器/沙箱中运行
vitest --browser,即使漏洞被利用,损失也被限制在容器内 - 审查测试依赖:定期审计
vitest.config中注册的自定义 provider 和插件 - 最小权限原则:
allowWrite白名单应尽可能窄——仅包含测试明确需要的输出目录 - 监控异常文件操作:在 CI/CD 中监控
/tmp、~/.ssh、环境变量文件等敏感位置的异常写入

