From 2f7c86182b2f543474424b0c7504522cc7d76deb Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:10:37 +0900 Subject: [PATCH 1/4] test: add failing test --- playground/fs-serve/__tests__/fs-serve.spec.ts | 9 +++++++++ playground/fs-serve/unsafe.map | 3 +++ 2 files changed, 12 insertions(+) create mode 100644 playground/fs-serve/unsafe.map diff --git a/playground/fs-serve/__tests__/fs-serve.spec.ts b/playground/fs-serve/__tests__/fs-serve.spec.ts index 0b145e35dc58f6..201f40d2ca177b 100644 --- a/playground/fs-serve/__tests__/fs-serve.spec.ts +++ b/playground/fs-serve/__tests__/fs-serve.spec.ts @@ -103,6 +103,15 @@ describe.runIf(isServe)('invalid request', () => { expect(response).toContain('HTTP/1.1 403 Forbidden') }) + test('should not allow relative path traversal with optimize deps sourcemap handler', async () => { + const response = await sendRawRequest( + viteTestUrl, + path.posix.join('/@fs/', root) + + '/node_modules/.vite/deps/../../../unsafe.map', + ) + expect(response).not.toContain('unsafe') + }) + test('should deny request to HTML file outside root by default with relative path', async () => { const response = await sendRawRequest(viteTestUrl, '/../unsafe.html') expect(response).toContain('HTTP/1.1 403 Forbidden') diff --git a/playground/fs-serve/unsafe.map b/playground/fs-serve/unsafe.map new file mode 100644 index 00000000000000..1f012ec0e1b34b --- /dev/null +++ b/playground/fs-serve/unsafe.map @@ -0,0 +1,3 @@ +{ + "key": "unsafe" +} From f3be3209937140eac6f45159608053d969e1db4e Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:34:25 +0900 Subject: [PATCH 2/4] fix: avoid path traversal with optimize deps sourcemap handler --- .../vite/src/node/server/middlewares/transform.ts | 4 ++++ playground/fs-serve/__tests__/fs-serve.spec.ts | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/server/middlewares/transform.ts b/packages/vite/src/node/server/middlewares/transform.ts index e6c7e3fdb24246..b1ba6d013cafa9 100644 --- a/packages/vite/src/node/server/middlewares/transform.ts +++ b/packages/vite/src/node/server/middlewares/transform.ts @@ -155,6 +155,10 @@ export function transformMiddleware( const sourcemapPath = url.startsWith(FS_PREFIX) ? fsPathFromId(url) : normalizePath(path.resolve(server.config.root, url.slice(1))) + // url may contain relative path that may resolve outside of the optimized deps directory + if (!depsOptimizer.isOptimizedDepFile(sourcemapPath)) { + return next() + } try { const map = JSON.parse( await fsp.readFile(sourcemapPath, 'utf-8'), diff --git a/playground/fs-serve/__tests__/fs-serve.spec.ts b/playground/fs-serve/__tests__/fs-serve.spec.ts index 201f40d2ca177b..a3e112a25b12b6 100644 --- a/playground/fs-serve/__tests__/fs-serve.spec.ts +++ b/playground/fs-serve/__tests__/fs-serve.spec.ts @@ -109,7 +109,16 @@ describe.runIf(isServe)('invalid request', () => { path.posix.join('/@fs/', root) + '/node_modules/.vite/deps/../../../unsafe.map', ) - expect(response).not.toContain('unsafe') + expect(response).toContain('HTTP/1.1 403 Forbidden') + }) + + test('should not allow backslash relative path traversal with optimize deps sourcemap handler', async () => { + const response = await sendRawRequest( + viteTestUrl, + path.posix.join('/@fs/', root) + + '/node_modules/.vite/deps/..\\..\\..\\unsafe.map', + ) + expect(response).toContain('HTTP/1.1 403 Forbidden') }) test('should deny request to HTML file outside root by default with relative path', async () => { From e2b52aa8d90afa0416c2ad79817cd558e76c817f Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Mon, 6 Apr 2026 19:01:58 +0900 Subject: [PATCH 3/4] test: windows --- .../fs-serve/__tests__/fs-serve.spec.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/playground/fs-serve/__tests__/fs-serve.spec.ts b/playground/fs-serve/__tests__/fs-serve.spec.ts index a3e112a25b12b6..a623a5084b13f3 100644 --- a/playground/fs-serve/__tests__/fs-serve.spec.ts +++ b/playground/fs-serve/__tests__/fs-serve.spec.ts @@ -112,14 +112,17 @@ describe.runIf(isServe)('invalid request', () => { expect(response).toContain('HTTP/1.1 403 Forbidden') }) - test('should not allow backslash relative path traversal with optimize deps sourcemap handler', async () => { - const response = await sendRawRequest( - viteTestUrl, - path.posix.join('/@fs/', root) + - '/node_modules/.vite/deps/..\\..\\..\\unsafe.map', - ) - expect(response).toContain('HTTP/1.1 403 Forbidden') - }) + test.runIf(isWindows)( + 'should not allow backslash relative path traversal with optimize deps sourcemap handler', + async () => { + const response = await sendRawRequest( + viteTestUrl, + path.posix.join('/@fs/', root) + + '/node_modules/.vite/deps/..\\..\\..\\unsafe.map', + ) + expect(response).toContain('HTTP/1.1 403 Forbidden') + }, + ) test('should deny request to HTML file outside root by default with relative path', async () => { const response = await sendRawRequest(viteTestUrl, '/../unsafe.html') From fb4b5d78ed78c33ce86f2f17e7ff4c104f56d138 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Mon, 6 Apr 2026 19:13:49 +0900 Subject: [PATCH 4/4] test: non-windows --- .../fs-serve/__tests__/fs-serve.spec.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/playground/fs-serve/__tests__/fs-serve.spec.ts b/playground/fs-serve/__tests__/fs-serve.spec.ts index a623a5084b13f3..f2953e7acf1e18 100644 --- a/playground/fs-serve/__tests__/fs-serve.spec.ts +++ b/playground/fs-serve/__tests__/fs-serve.spec.ts @@ -112,17 +112,19 @@ describe.runIf(isServe)('invalid request', () => { expect(response).toContain('HTTP/1.1 403 Forbidden') }) - test.runIf(isWindows)( - 'should not allow backslash relative path traversal with optimize deps sourcemap handler', - async () => { - const response = await sendRawRequest( - viteTestUrl, - path.posix.join('/@fs/', root) + - '/node_modules/.vite/deps/..\\..\\..\\unsafe.map', - ) + test('should not allow backslash relative path traversal with optimize deps sourcemap handler', async () => { + const response = await sendRawRequest( + viteTestUrl, + path.posix.join('/@fs/', root) + + '/node_modules/.vite/deps/..\\..\\..\\unsafe.map', + ) + if (isWindows) { expect(response).toContain('HTTP/1.1 403 Forbidden') - }, - ) + } else { + // should be 404 fallback + expect(response).toContain('Cache-Control: no-cache') + } + }) test('should deny request to HTML file outside root by default with relative path', async () => { const response = await sendRawRequest(viteTestUrl, '/../unsafe.html')