diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index b5eb087b7b467c..fa7834b8fec07c 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -123,7 +123,7 @@ export function serveRawFsMiddleware( // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` return function viteServeRawFsMiddleware(req, res, next) { - let url = decodeURI(req.url!) + let url = decodeURIComponent(req.url!) // In some cases (e.g. linked monorepos) files outside of root will // reference assets that are also out of served root. In such cases // the paths are rewritten to `/@fs/` prefixed paths and must be served by diff --git a/playground/fs-serve/__tests__/fs-serve.spec.ts b/playground/fs-serve/__tests__/fs-serve.spec.ts index 747b46ecbb8287..25c274be05ca2e 100644 --- a/playground/fs-serve/__tests__/fs-serve.spec.ts +++ b/playground/fs-serve/__tests__/fs-serve.spec.ts @@ -52,6 +52,11 @@ describe.runIf(isServe)('main', () => { expect(await page.textContent('.unsafe-fs-fetch-status')).toBe('403') }) + test('unsafe fs fetch with special characters (#8498)', async () => { + expect(await page.textContent('.unsafe-fs-fetch-8498')).toBe('') + expect(await page.textContent('.unsafe-fs-fetch-8498-status')).toBe('403') + }) + test('nested entry', async () => { expect(await page.textContent('.nested-entry')).toBe('foobar') }) diff --git a/playground/fs-serve/root/src/index.html b/playground/fs-serve/root/src/index.html index 951e14ad2cce91..a0240bf7c89bba 100644 --- a/playground/fs-serve/root/src/index.html +++ b/playground/fs-serve/root/src/index.html @@ -27,6 +27,8 @@