diff --git a/packages/playground/fs-serve/__tests__/fs-serve.spec.ts b/packages/playground/fs-serve/__tests__/fs-serve.spec.ts
index eba1e441881710..77940b40b3aebd 100644
--- a/packages/playground/fs-serve/__tests__/fs-serve.spec.ts
+++ b/packages/playground/fs-serve/__tests__/fs-serve.spec.ts
@@ -37,6 +37,13 @@ describe('main', () => {
expect(await page.textContent('.unsafe-fetch-status')).toBe('403')
})
+ test('unsafe fetch with special characters (#8498)', async () => {
+ expect(await page.textContent('.unsafe-fetch-8498')).toMatch(
+ '403 Restricted'
+ )
+ expect(await page.textContent('.unsafe-fetch-8498-status')).toBe('403')
+ })
+
test('safe fs fetch', async () => {
expect(await page.textContent('.safe-fs-fetch')).toBe(stringified)
expect(await page.textContent('.safe-fs-fetch-status')).toBe('200')
@@ -54,6 +61,11 @@ describe('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/packages/playground/fs-serve/root/src/index.html b/packages/playground/fs-serve/root/src/index.html
index 951e14ad2cce91..6939e0f4b09ed9 100644
--- a/packages/playground/fs-serve/root/src/index.html
+++ b/packages/playground/fs-serve/root/src/index.html
@@ -17,6 +17,8 @@
Safe Fetch Subdirectory
Unsafe Fetch
+
+
Safe /@fs/ Fetch
@@ -27,6 +29,8 @@ Safe /@fs/ Fetch
Unsafe /@fs/ Fetch
+
+
Nested Entry
@@ -83,6 +87,19 @@ Denied
console.error(e)
})
+ // outside of allowed dir with special characters #8498
+ fetch('/src/%2e%2e%2funsafe%2etxt')
+ .then((r) => {
+ text('.unsafe-fetch-8498-status', r.status)
+ return r.text()
+ })
+ .then((data) => {
+ text('.unsafe-fetch-8498', data)
+ })
+ .catch((e) => {
+ console.error(e)
+ })
+
// imported before, should be treated as safe
fetch('/@fs/' + ROOT + '/safe.json')
.then((r) => {
@@ -106,6 +123,16 @@ Denied
console.error(e)
})
+ // outside root with special characters #8498
+ fetch('/@fs/' + ROOT + '/root/src/%2e%2e%2f%2e%2e%2funsafe%2ejson')
+ .then((r) => {
+ text('.unsafe-fs-fetch-8498-status', r.status)
+ return r.json()
+ })
+ .then((data) => {
+ text('.unsafe-fs-fetch-8498', JSON.stringify(data))
+ })
+
// not imported before, inside root with special characters, treated as safe
fetch(
'/@fs/' +
diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts
index a7a541ab343e4b..659dfbae62ecfe 100644
--- a/packages/vite/src/node/server/middlewares/static.ts
+++ b/packages/vite/src/node/server/middlewares/static.ts
@@ -78,7 +78,7 @@ export function serveStaticMiddleware(
return next()
}
- const url = decodeURI(req.url!)
+ const url = decodeURIComponent(req.url!)
// apply aliases to static requests as well
let redirected: string | undefined
@@ -121,7 +121,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