From b825313243dc55757e8d7c0f9e7a792135e13473 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 14 Oct 2026 16:49:13 +0200 Subject: [PATCH] fix(mocker): harden manual and redirect mocks [backport to v4] (#11580) --- packages/mocker/src/node/interceptorPlugin.ts | 3 ++- packages/mocker/src/utils.ts | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/mocker/src/node/interceptorPlugin.ts b/packages/mocker/src/node/interceptorPlugin.ts index 13054dc9a..f3590a73f 100644 --- a/packages/mocker/src/node/interceptorPlugin.ts +++ b/packages/mocker/src/node/interceptorPlugin.ts @@ -2,6 +2,7 @@ import type { Plugin } from 'vite' import type { MockedModuleSerialized } from '../registry' import { readFile } from 'node:fs/promises' import { join } from 'node:path/posix' +import { slash } from '@vitest/utils/helpers' import { isFileLoadingAllowed } from 'vite' import { ManualMockedModule, MockerRegistry } from '../registry' import { cleanUrl, createManualModuleSource } from '../utils' @@ -78,7 +79,7 @@ export function interceptorPlugin(options: InterceptorPluginOptions = {}): Plugi else { if (event.type === 'redirect') { const redirectUrl = new URL(event.redirect) - const redirect = join(server.config.root, redirectUrl.pathname) + const redirect = join(server.config.root, slash(redirectUrl.pathname)) // the redirect is served through the `load` hook below, so it must // stay inside the file-serving allowlist and never escape the root if (!isFileLoadingAllowed(server.config, redirect)) { diff --git a/packages/mocker/src/utils.ts b/packages/mocker/src/utils.ts index 084fc7f42..60d453c67 100644 --- a/packages/mocker/src/utils.ts +++ b/packages/mocker/src/utils.ts @@ -5,12 +5,12 @@ export function cleanUrl(url: string): string { export function createManualModuleSource(moduleUrl: string, exports: string[], globalAccessor = '"__vitest_mocker__"'): string { const source = ` -const __factoryModule__ = await globalThis[${globalAccessor}].getFactoryModule("${moduleUrl}"); +const __factoryModule__ = await globalThis[${globalAccessor}].getFactoryModule(${JSON.stringify(moduleUrl)}); ` const keys = exports .map((name, index) => { - return `let __${index} = __factoryModule__["${name}"] -export { __${index} as "${name}" }` + return `let __${index} = __factoryModule__[${JSON.stringify(name)}] +export { __${index} as ${JSON.stringify(name)} }` }) .join('\n') let code = `${source}\n${keys}` @@ -19,7 +19,7 @@ export { __${index} as "${name}" }` if (__factoryModule__.__factoryPromise != null) { __factoryModule__.__factoryPromise.then((resolvedModule) => { ${exports.map((name, index) => { - return `__${index} = resolvedModule["${name}"];` + return `__${index} = resolvedModule[${JSON.stringify(name)}];` }).join('\n')} }) } -- 2.54.0