Repository navigation
chore(deps): update dependency vitest to v4 [security] - #8289
renovate[bot] wants to merge 1 commit into
Conversation
|
📊 Benchmark resultsComparing with 20fb23d
|
Autoclosing SkippedThis PR has been flagged for autoclosing. However, it is being skipped due to the branch being already modified. Please close/delete it manually or report a bug if you think this is in error. |
31db0ad to
cb7b2c6
Compare
e6bc32d to
ff845b8
Compare
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
ff845b8 to
99b6324
Compare
ef2811a to
2875436
Compare
2875436 to
277428d
Compare
Opened by Netliloop run [#392](https://netliloop.netlify.app/#/runs/392) (security-scan), asked in [Slack](https://slack.com/archives/C095D1JL480/p1791299459766719?thread_ts=1791298825.895999&cid=C095D1JL480) ### Why - `vitest@3.2.7` carries [GHSA-82fw-gwwq-j7x9](GHSA-82fw-gwwq-j7x9) (`@vitest/mocker` path traversal, patched in 4.1.11). Renovate's [#8289](#8289) and [#8320](#8320) target it but their lockfiles no longer resolve against `main` (`npm ci` fails on every rebase), and Dependabot's [#8570](#8570) jumps to vitest 5 and fails typecheck, lint and 12 unit tests. - `oxfmt@0.61.0` pins `tinypool@2.1.0`, which carries the critical [GHSA-85c8-ppgw-ccpr](GHSA-85c8-ppgw-ccpr); the only way past it is an oxfmt version that pins `tinypool >= 2.1.2`. - `npm audit` on `main` lists `vitest`, `@vitest/mocker`, `@vitest/coverage-v8` and `tinypool`; on this branch none of them appear. ### What changed - `vitest` and `@vitest/coverage-v8` go to `^4.1.11`, the first patched release (vitest 5 is a week old). - `oxfmt` goes to `0.72.0`, which pins `tinypool@2.2.0`; running it reformats two files under `src/commands/logs/sources/`. - vitest 4 removed `poolOptions`, so `threads.singleThread: true` becomes `maxWorkers: 1` in both configs: still one worker thread, one file at a time. This is not a byte-for-byte port. The migration guide's equivalent adds `isolate: false`, but in vitest 4 that shares module mocks across files and fails 38 unit tests here, so file isolation stays on and the comment in each config says why. Each file now gets a fresh worker; the Windows-hang TODO beside the pin still applies and CI's Windows jobs on this PR are the check. - Transitively, vitest now carries its own nested Vite 8.3.3 (rolldown) instead of the top-level Vite 7.3.5, so test-file transforms change engine. - Coverage reports change shape (vitest 4 drops `coverage.all` and uses AST remapping), so the Codecov numbers on this PR will move; that is the tooling, not a regression. - vitest 4 constructs mocks called with `new`, so the `NetlifyAPI` and `LocalState` mock implementations in three test files become `function` expressions instead of arrow functions. - `toThrowError` is marked `@deprecated` ("Alias for `expect.toThrow`") in `@vitest/expect` 4 and so flagged by `@typescript-eslint/no-deprecated`; 38 call sites in `tests/` switch to `toThrow` with the same arguments. - Supersedes #8289, #8320 and #8570, which can be closed when this merges. ### How we verified - `npm ci --dry-run`: the committed lockfile is in sync with `package.json` (Renovate's #8289 and #8320 fail exactly here). - `npm run build`, `npm run typecheck`, `npm run lint`, `npm run format:check`: all exit 0. - `CI=true npm run test:unit -- --coverage`: 80 files, 667 tests passed, the same counts as `CI=true npm run test:unit` on `main`. With the migration guide's `isolate: false` the same command fails 38 tests in 9 files (leaked module mocks), which is why isolation stays on. - `CI=true npx vitest run --retry=3 --coverage tests/integration/commands/env/env.test.ts tests/integration/commands/dev/dev-forms-and-redirects.test.ts`: 2 files, 32 tests passed, so `--retry`, `--coverage` and the integration harness work on vitest 4 - `CI=true npm run test:integration -- --shard=1/4` here: 24 failures in 6 files, and the same 6 files fail with the same 24 tests on `main` under vitest 3 (this machine has a linked Netlify site and no git identity, which `build`, `clone` and `link` tests depend on); the full set is CI's 8 green shards. - `npx vitest list --config vitest.e2e.config.ts`: the e2e config loads and lists 5 tests. - `CI=true npx vitest run --config vitest.e2e.config.ts -t "npm →"` runs through verdaccio publish, `npm install`, `netlify --help` and `netlify link` on vitest 4 and fails at the `netlify unlink` assertion (`expected ... to contain 'Run netlify link to link it'`). The identical run on `main` with vitest 3 fails at the same line: this machine has a globally linked Netlify site (`main` received `Unlinked from netliloop`) and, once unlinked, the CLI running under `npx` phrases the hint as `Run npx netlify link` (the branch received that). The failure is the environment, not the upgrade; the e2e suite is unverified here. - `npm audit --json`: on `main` it lists `vitest` (critical, range 0.0.95 - 4.1.10), `@vitest/mocker`, `@vitest/coverage-v8` and `tinypool <=2.1.1`; on this branch none of the four appear and `npm ls tinypool` shows only `oxfmt@0.72.0 → tinypool@2.2.0`. 39 advisories remain, unchanged from `main`; the two critical ones are `proxy-addr` (Dependabot's #8567 fixes it) and `shell-quote`. ### What is left to test - The full integration suite, the e2e suite, and unit/integration on macOS and Windows (where the single-thread pin matters most) could not run here; on this PR CI ran them and all 34 checks are green (unit on ubuntu/macOS/Windows, 8 integration shards, e2e, lint, format, typecheck, verify-docs, package-size). ### Risk `low`: development tooling only; nothing in `dist/` or the published package changes except the two reformatted files, whose formatting-only diff is in the branch. No Linear issue: a self-contained dependency fix the CLI team can merge from this description. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Netliloop <netliloop@netlify.com> Co-authored-by: Sarah Etter <sarah.etter@netlify.com>
|
Superseded by #8573 (vitest ^4.1.11, oxfmt 0.72.0 → tinypool 2.2.0), which has merged. |
This PR contains the following updates:
^3.2.4→^4.0.0Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock
CVE-2026-84373 / GHSA-82fw-gwwq-j7x9
More information
Details
Summary
@vitest/mockerregisters a redirect mock's target path without validating itagainst the dev server's file-serving allowlist. An attacker who can reach the
dev server's WebSocket can register a redirect mock pointing outside the project
root; when the mocked module is requested, the plugin's
loadhook returnsreadFile(<attacker path>)as the module source, disclosing local files.This is exploitable without authentication only through the public
mockerPlugin/ standaloneinterceptorPluginexports (used by third-party devservers), which register the handler on Vite's unauthenticated HMR socket.
Vitest's own browser mode registers mocks over a token-authenticated RPC and
is not remotely reachable by default (see Scope).
Affected code
packages/mocker/src/node/interceptorPlugin.ts.The
loadhook is the file-read sink:mock.redirectis derived from client input at registration time with noboundary check:
There is no
server.fs.allow/server.fs.denycheck and no assertion that theresolved path stays within the project root.
Registration paths and trust boundaries
mockerPlugin/interceptorPlugin(unauthenticated). InconfigureServer, the plugin registersserver.ws.on('vitest:interceptor:register', …)on Vite's HMR WebSocket. That socket performs no token, Origin, or same-origin
check, so any client that can reach it can register a redirect mock. This is
the path the "unauthenticated" impact applies to.
(
registerMock), which sits behind a per-run token (isValidApiRequest, arandom
api.token). The interceptor'sconfigureServersocket is not used forregistration here (in v5 it does not run at all, as the plugin is injected per
environment). The same missing boundary check exists on the authenticated RPC
path, but reaching it requires the token, so it is not a remote-unauthenticated
read.
Path handling
new URL(redirect).pathnamecombined withjoin(root, pathname)does notconfine reads to the root:
file:,http:) are normalized by WHATWG URL,so
..segments are collapsed and the result stays under the root. Payloads ofthe form
file:///../../etc/passwddo not escape...inpathname, sojoin(root, "../../…/etc/passwd")resolves outside the root and reads anarbitrary file.
Even without escaping the root, the missing
server.fscheck allows reading anyin-root file the dev server would otherwise refuse to serve (for example an
in-root
.envor source that is denied byserver.fs.deny).Scope / preconditions
localhostbydefault and is not reachable from the network unless the developer exposes it
(
server.host/0.0.0.0, a LAN bind, or a proxy).and CORS protections entirely and can both register the mock and read the
response.
by Vite defaults: the default CORS origin allowlist is limited to
localhostorigins, and
server.allowedHostsblocks DNS-rebinding, so a cross-origin pagecannot read the file contents back.
Impact
Disclosure of local files readable by the dev-server process (source, in-root
.env/secrets, and, via the opaque-scheme payload, files outside the projectroot). No integrity or availability impact.
Affected versions
Present since
@vitest/mockerwas introduced.@vitest/mocker>= 2.1.0 (shipped invitestand@vitest/browser>= 2.1.0), through 4.1.x and the 5.0.0 pre-releases.maintained and are not planned to receive the fix.
Fix
Validate the resolved redirect target against Vite's file-serving allowlist
(
isFileLoadingAllowed) before registering it, at every registration site, andstop registering the interceptor WebSocket events in Vitest's browser mode
(mocks there flow through the authenticated RPC).
Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
vitest-dev/vitest (vitest)
v4.1.11Compare Source
🐞 Bug Fixes
View changes on GitHub
v4.1.10Compare Source
🐞 Bug Fixes
View changes on GitHub
v4.1.9Compare Source
🐞 Bug Fixes
importOriginalwith optimizer and query import [backport to v4] - by Hiroshi Ogawa, David Harris, Codexand Vladimir in #10546 (a5180)View changes on GitHub
v4.1.8Compare Source
🐞 Bug Fixes
cdpAPI whenallowWrite/allowExec: false[backport to v4] - by @hi-ogawa and Codex in #10450 (e4067)View changes on GitHub
v4.1.7Compare Source
🐞 Bug Fixes
View changes on GitHub
v4.1.6Compare Source
🐞 Bug Fixes
ToMatchScreenshotResolvePath- by @macarie and @sheremet-va in #10138 (31882)sequence.concurrent: truewith top-leveltest(..., { concurrent: false })+ depreactesequentialtest API and options - by @hi-ogawa, Codex and @sheremet-va in #10196 (2847d)🏎 Performance
View changes on GitHub
v4.1.5Compare Source
🚀 Experimental Features
instrumenteroption - by @BartWaardenburg and @AriPerkkio in #10119 (0e0ff)🐞 Bug Fixes
vi.defineHelpercalled as object method - by @hi-ogawa in #10163 (122c2)agentreporter tominimal- by @sheremet-va in #10157 (663b9)View changes on GitHub
v4.1.4Compare Source
🚀 Features
skipFullif agent detected - by @hi-ogawa in #10018 (53757)assertionas a public field - by @sheremet-va in #10095 (a120e)🐞 Bug Fixes
expect(..., message)consistent as error message prefix - by @hi-ogawa and Codex in #10068 (a1b5f)View changes on GitHub
v4.1.3Compare Source
🚀 Experimental Features
experimental.preParseflag - by @sheremet-va in #10070 (78273)browser.locators.exactoption - by @sheremet-va in #10013 (48799)TestAttachment.bodyEncoding- by @hi-ogawa in #9969 (89ca0)🐞 Bug Fixes
expect.pollinterval - by @hi-ogawa and Claude Sonnet 4.6 in #10022 (3f5bf)@vitest/coverage-v8and@vitest/coverage-istanbulas optional dependency - by @alan-agius4 in #10025 (146d4)defineHelperfor webkit async stack trace + update playwright 1.59.0 - by @hi-ogawa in #10036 (5a5fa)JestExtendError.contextfrom verbose error reporting - by @hi-ogawa in #9983 (66751)vitest- by @hi-ogawa and Codex in #10042 (691d3)View changes on GitHub
v4.1.2Compare Source
This release bumps Vitest's
flattedversion and removes version pinning to resolveflatted's CVE related issues (#9975).🐞 Bug Fixes
setupFilesfrom parent directory - by @hi-ogawa in #9960 (7aa93)toMatchScreenshotcan't capture a stable screenshot - by @macarie in #9847 (faace)coverageConfigDefaultsvalues and types - by @Arthie in #9940 (b3c99)View changes on GitHub
v4.1.1Compare Source
🚀 Features
matchesTagsFilterto test if the current filter matches tags - by @sheremet-va in #9913 (eec53)experimental.vcsProvider- by @sheremet-va in #9928 (56115)🐞 Bug Fixes
TestProject.testFilesListinternal properly - by @sapphi-red in #9867 (54f26)use- by @oilater in #9831 and #9861 (633ae)vi.advanceTimersto the preview provider - by @sheremet-va in #9891 (1bc3e)--standalonemode without running tests - by @sheremet-va in #9911 (e78ad)body- by @sheremet-va in #9912 (6fdb2)retry.conditionRegExp serialization issue - by @nstepien and @hi-ogawa in #9942 (7b605)testreturn as tests - by @sheremet-va in #9871 (141e7)View changes on GitHub
v4.1.0Compare Source
Vitest 4.1 is out!
This release page lists all changes made to the project during the 4.1 beta. To get a review of all the new features, read our blog post.
🚀 Features
setTickModeto timer controls - by @atscott and @sheremet-va in #8726 (4b480)toTestSpecificationto reported tasks - by @sheremet-va in #9464 (1a470)vi.mockorvi.hoistedare declared outside of top level of the module - by @sheremet-va in #9387 (5db54)aroundEachandaroundAllhooks - by @sheremet-va in #9450 (2a8cb)neworallin--updateflag - by @sheremet-va in #9543 (a5acf)metain test options - by @sheremet-va in #9535 (7d622)test.extendsyntax - by @sheremet-va in #9550 (e5385)vitest listto statically collect tests instead of running files to collect them - by @sheremet-va in #9630 (7a8e7)--detect-async-leaks- by @AriPerkkio in #9528 (c594d)mockThrowandmockThrowOnce- by @thor-juhasz and @sheremet-va in #9512 (61917)update: "none"and add docs about snapshots behavior on CI - by @hi-ogawa in #9700 (05f18)launchOptionswithconnectOptions- by @hi-ogawa in #9702 (f0ff1)page/locator.markAPI to enhance playwright trace - by @hi-ogawa in #9652 (d0ee5)testinexperimental_parseSpecification- by @jgillick and Jeremy Gillick in #9235 (2f367)createSpecification- by @sheremet-va in #9336 (c8e6c)runTestFilesas alternative torunTestSpecifications- by @sheremet-va in #9443 (43d76)allowWriteandallowExecoptions toapi- by @sheremet-va in #9350 (20e00)toTestSpecification- by @sheremet-va in #9627 (6f17d)userEvent.wheelAPI - by @macarie in #9188 (66080)filterNodeoption to prettyDOM for filtering browser assertion error output - by @Copilot, sheremet-va and @sheremet-va in #9475 (d3220)detailsPanelPositionoption and button - by @shairez in #9525 (c8a31)findElementand enable strict mode in webdriverio and preview - by @sheremet-va in #9677 (c3f37)ignore start/stopignore hints - by @AriPerkkio in #9204 (e59c9)coverage.changedoption to report only changed files - by @kykim00 and @AriPerkkio in #9521 (1d939)onModuleRunnerhook toworker.init- by @sheremet-va in #9286 (e977f)importDurations: { limit, print }options - by @hi-ogawa, Claude Opus 4.6 and @sheremet-va in #9401 (7e10f)importDurations- by @hi-ogawa and Claude Opus 4.6 in #9533 (3f7a5)beforeAll/afterAll- by @sheremet-va in #9572 (c8339)agentreporter to reduce ai agent token usage - by @cpojer in #9779 (3e9e0)retryoptions - by @MazenSamehR, Matan Shavit, @AriPerkkio and @sheremet-va in #9370 (9e4cf)🐞 Bug Fixes
meta.urlincreateRequire- by @sheremet-va in #9441 (e3422)external/noExternalduringconfigEnvironmenthook - by @hi-ogawa and Claude Opus 4.6 in #9508 (59ea2)browser.isolateis used - by @sheremet-va in #9410 (3d48e)vi.mock({ spy: true })node v8 coverage - by @hi-ogawa, hi-ogawa and Claude Opus 4.6 in #9541 (687b6).namefrom statically collected test - by @sheremet-va in #9596 (b66ff)expect.soft- by @iumehara, @hi-ogawa and Claude Opus 4.6 in #9231 (3eb2c)sequence.shuffle.testsis enabled - by @kaigritun, Kai Gritun and @sheremet-va in #9576 (8182b)expect/src/utilsfromvitest- by @hi-ogawa in #9616 (48739)Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.