Skip to content

CVE-2026-84373 vitest: Path traversal / arbitrary file read via @vitest/mocker redirect mocks (4.1.0 -> 4.1.11) - #1124

Open
dependabot[bot] wants to merge 1 commit into
masterfrom
dependabot/npm_and_yarn/openam-ui/openam-ui-js-sdk/multi-d0c2d048a7
Open

CVE-2026-84373 vitest: Path traversal / arbitrary file read via @vitest/mocker redirect mocks (4.1.0 -> 4.1.11)#1124
dependabot[bot] wants to merge 1 commit into
masterfrom
dependabot/npm_and_yarn/openam-ui/openam-ui-js-sdk/multi-d0c2d048a7

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Sep 10, 2026

Copy link
Copy Markdown
Contributor

Bumps vitest in openam-ui-js-sdk from 4.1.0 to 4.1.11, carrying the whole pinned @vitest/* set — expect, mocker, pretty-format, runner, snapshot, spy, utils — to the same version. The alert is on @vitest/mocker, which is an exact-version dependency of vitest, so the two have to move together.

  • CVE-2026-84373 / GHSA-82fw-gwwq-j7x9 — CWE-22, CVSS 3.1 5.9 medium (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N), EPSS 0.38%; affects @vitest/mocker and vitest >= 2.1.0, < 4.1.11, patched in 4.1.11 (and 5.0.0-rc.2). 2.1.x and 3.x are unmaintained and will not get the fix.

Path traversal / arbitrary file read via redirect mocks

@vitest/mocker's interceptorPlugin takes a redirect mock's target path from client input and registers it without any boundary check:

if (event.type === 'redirect') {
  const redirectUrl = new URL(event.redirect)
  event.redirect = join(server.config.root, redirectUrl.pathname)
}
registry.register(event)

The plugin's load hook is the file-read sink — return readFile(mock.redirect, 'utf-8') — so whatever path was registered is handed back as module source the next time the mocked module is requested.

join(root, new URL(redirect).pathname) is not a containment guard. For a special scheme (file:, http:) WHATWG URL collapses .. during parsing, so file:///../../etc/passwd stays under the root; for an opaque (non-special) scheme the .. segments survive into pathname, and join(root, "../../…/etc/passwd") resolves outside the project root. And even without escaping the root, there is no server.fs.allow / server.fs.deny consultation at all, so an in-root file the dev server would otherwise refuse to serve — an .env, a denied source file — is readable too.

Two registration channels reach that code, with different trust:

  • public mockerPlugin / standalone interceptorPlugin (the export third-party dev servers embed): configureServer hangs server.ws.on('vitest:interceptor:register', …) on Vite's HMR WebSocket, which does no token, Origin, or same-origin check. This is the unauthenticated path the advisory scores.
  • Vitest browser mode: mocks register over the browser RPC behind a per-run random api.token, so the same missing check is only reachable with the token.

4.1.11 validates the resolved target with Vite's isFileLoadingAllowed(server.config, redirect) before registering and drops the registration if it fails, adds checkFileAccess() on the browser RPC path, and makes browser mode pass the new registerWebSocketEvents: false so the raw dev-server socket stops accepting mock registration at all.

Scope in OpenAM

vitest is a dev-only dependency of openam-ui-js-sdk — every entry in the bumped subtree is "dev": true, the Maven build only runs it via npm run test:run (vitest run, single pass, no watch and no API server), and the app/lib assemblies zip target/app and target/lib only. Nothing from this tree ships in the WAR.

The vulnerable code is not reachable in this module as configured:

  • vitest.config.js runs plain node tests (environment: 'jsdom'), not browser mode. @vitest/browser-playwright / -preview / -webdriverio are optional peers and none is installed; msw is not installed either.
  • Upstream wires interceptorPlugin up from exactly two places — @vitest/browser's server and the public mockerPlugin export documented for third-party dev servers. The core node runner registers neither, so vitest run never opens a vitest:interceptor:register handler.
  • npm run dev does bind a dev server to every interface (vite --host 0.0.0.0), but that is plain Vite serving the demo app under vite.config.ts (plugins: [react()], server.allowedHosts: ["localhost", "openam.example.org"]) — no mocker plugin is loaded there.

So the bump is hardening rather than an active-exploit fix: it takes the toolchain out of the affected range and keeps the door shut if the module later adopts browser mode or the public mocker plugin. No sibling module is left behind — neither openam-ui-ria nor openam-ui-api resolves vitest at all. The module also sat exactly on 4.1.0 and was therefore already clear of GHSA-5xrq-8626-4rwp (>= 4.0.0, < 4.1.0); CVE-2026-84373 is the only vitest advisory that applied.

Verified

  • the package.json floor moves ^4.1.0 -> ^4.1.11, so a fresh npm install cannot resolve back into the affected range
  • all nine changed integrity hashes match npm view <pkg> dist.integrity: vitest@4.1.11, @vitest/expect@4.1.11, @vitest/mocker@4.1.11, @vitest/pretty-format@4.1.11, @vitest/runner@4.1.11, @vitest/snapshot@4.1.11, @vitest/spy@4.1.11, @vitest/utils@4.1.11, tinyrainbow@3.1.1
  • the vite peer range tightens from ^6.0.0 || ^7.0.0 || ^8.0.0-0 to ^6.0.0 || ^7.0.0 || ^8.0.0; the module resolves vite@8.0.16, which satisfies both, so nothing else has to move
  • @vitest/coverage-istanbul and @vitest/coverage-v8 show up as new optional peers of vitest; neither is installed and neither is required
  • tinyrainbow 3.1.0 -> 3.1.1 rides along on the raised ^3.0.3 -> ^3.1.0 floor of the @vitest/* packages; it is the only non-@vitest package the lock file touches
  • beyond the advisory, 4.1.1–4.1.11 also pick up runner fixes that matter for CI stability: the worker-crash hang in the pool (#10543), importOriginal with the deps optimizer (#10546) and the restored global concurrency limit for test lifecycle hooks (#10992)

Upstream: advisory · #10974 / fe5a11d (v4 backport) · #10972 (main) · 4.1.11 release

Bumps [@vitest/mocker](https://github.com/vitest-dev/vitest/tree/HEAD/packages/mocker) to 4.1.11 and updates ancestor dependency [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). These dependencies need to be updated together.


Updates `@vitest/mocker` from 4.1.0 to 4.1.11
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.11/packages/mocker)

Updates `vitest` from 4.1.0 to 4.1.11
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.11/packages/vitest)

---
updated-dependencies:
- dependency-name: "@vitest/mocker"
  dependency-version: 4.1.11
  dependency-type: indirect
- dependency-name: vitest
  dependency-version: 4.1.11
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code labels Sep 10, 2026
@vharseko vharseko changed the title Bump @vitest/mocker and vitest in /openam-ui/openam-ui-js-sdk CVE-2026-84373 vitest: Path traversal / arbitrary file read via @vitest/mocker redirect mocks (4.1.0 -> 4.1.11) Sep 10, 2026
@vharseko vharseko added the security Security fix or hardening (CVE, GHSA, XSS/CSRF/SSRF) label Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code security Security fix or hardening (CVE, GHSA, XSS/CSRF/SSRF)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant