Skip to content

Commit 521bb39

Browse files
authored
fix: fs serve only edit pathname (fixes #9148) (#9654)
1 parent ed8d6a7 commit 521bb39

File tree

3 files changed

+59
-18
lines changed

3 files changed

+59
-18
lines changed

packages/playground/fs-serve/__tests__/fs-serve.spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ describe('main', () => {
2323
expect(await page.textContent('.safe-fetch-status')).toBe('200')
2424
})
2525

26+
test('safe fetch with query', async () => {
27+
expect(await page.textContent('.safe-fetch-query')).toMatch('KEY=safe')
28+
expect(await page.textContent('.safe-fetch-query-status')).toBe('200')
29+
})
30+
2631
test('safe fetch with special characters', async () => {
2732
expect(
2833
await page.textContent('.safe-fetch-subdir-special-characters')
@@ -54,6 +59,11 @@ describe('main', () => {
5459
expect(await page.textContent('.safe-fs-fetch-status')).toBe('200')
5560
})
5661

62+
test('safe fs fetch with query', async () => {
63+
expect(await page.textContent('.safe-fs-fetch-query')).toBe(stringified)
64+
expect(await page.textContent('.safe-fs-fetch-query-status')).toBe('200')
65+
})
66+
5767
test('safe fs fetch with special characters', async () => {
5868
expect(await page.textContent('.safe-fs-fetch-special-characters')).toBe(
5969
stringified

packages/playground/fs-serve/root/src/index.html

+25
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ <h2>Normal Import</h2>
77
<h2>Safe Fetch</h2>
88
<pre class="safe-fetch-status"></pre>
99
<pre class="safe-fetch"></pre>
10+
<pre class="safe-fetch-query-status"></pre>
11+
<pre class="safe-fetch-query"></pre>
1012

1113
<h2>Safe Fetch Subdirectory</h2>
1214
<pre class="safe-fetch-subdir-status"></pre>
@@ -25,6 +27,8 @@ <h2>Unsafe Fetch</h2>
2527
<h2>Safe /@fs/ Fetch</h2>
2628
<pre class="safe-fs-fetch-status"></pre>
2729
<pre class="safe-fs-fetch"></pre>
30+
<pre class="safe-fs-fetch-query-status"></pre>
31+
<pre class="safe-fs-fetch-query"></pre>
2832
<pre class="safe-fs-fetch-special-characters-status"></pre>
2933
<pre class="safe-fs-fetch-special-characters"></pre>
3034

@@ -58,6 +62,17 @@ <h2>Denied</h2>
5862
.then((data) => {
5963
text('.safe-fetch', JSON.stringify(data))
6064
})
65+
66+
// inside allowed dir with query, safe fetch
67+
fetch('/src/safe.txt?query')
68+
.then((r) => {
69+
text('.safe-fetch-query-status', r.status)
70+
return r.text()
71+
})
72+
.then((data) => {
73+
text('.safe-fetch-query', JSON.stringify(data))
74+
})
75+
6176
// inside allowed dir, safe fetch
6277
fetch('/src/subdir/safe.txt')
6378
.then((r) => {
@@ -127,6 +142,16 @@ <h2>Denied</h2>
127142
text('.safe-fs-fetch', JSON.stringify(data))
128143
})
129144

145+
// imported before with query, should be treated as safe
146+
fetch('/@fs/' + ROOT + '/safe.json?query')
147+
.then((r) => {
148+
text('.safe-fs-fetch-query-status', r.status)
149+
return r.json()
150+
})
151+
.then((data) => {
152+
text('.safe-fs-fetch-query', JSON.stringify(data))
153+
})
154+
130155
// not imported before, outside of root, treated as unsafe
131156
fetch('/@fs/' + ROOT + '/unsafe.json')
132157
.then((r) => {

packages/vite/src/node/server/middlewares/static.ts

+24-18
Original file line numberDiff line numberDiff line change
@@ -78,36 +78,40 @@ export function serveStaticMiddleware(
7878
return next()
7979
}
8080

81-
const url = decodeURIComponent(req.url!)
81+
const url = new URL(req.url!, 'http://example.com')
82+
const pathname = decodeURIComponent(url.pathname)
8283

8384
// apply aliases to static requests as well
84-
let redirected: string | undefined
85+
let redirectedPathname: string | undefined
8586
for (const { find, replacement } of server.config.resolve.alias) {
8687
const matches =
87-
typeof find === 'string' ? url.startsWith(find) : find.test(url)
88+
typeof find === 'string'
89+
? pathname.startsWith(find)
90+
: find.test(pathname)
8891
if (matches) {
89-
redirected = url.replace(find, replacement)
92+
redirectedPathname = pathname.replace(find, replacement)
9093
break
9194
}
9295
}
93-
if (redirected) {
96+
if (redirectedPathname) {
9497
// dir is pre-normalized to posix style
95-
if (redirected.startsWith(dir)) {
96-
redirected = redirected.slice(dir.length)
98+
if (redirectedPathname.startsWith(dir)) {
99+
redirectedPathname = redirectedPathname.slice(dir.length)
97100
}
98101
}
99102

100-
const resolvedUrl = redirected || url
101-
let fileUrl = path.resolve(dir, resolvedUrl.replace(/^\//, ''))
102-
if (resolvedUrl.endsWith('/') && !fileUrl.endsWith('/')) {
103+
const resolvedPathname = redirectedPathname || pathname
104+
let fileUrl = path.resolve(dir, resolvedPathname.replace(/^\//, ''))
105+
if (resolvedPathname.endsWith('/') && !fileUrl.endsWith('/')) {
103106
fileUrl = fileUrl + '/'
104107
}
105108
if (!ensureServingAccess(fileUrl, server, res, next)) {
106109
return
107110
}
108111

109-
if (redirected) {
110-
req.url = encodeURIComponent(redirected)
112+
if (redirectedPathname) {
113+
url.pathname = encodeURIComponent(redirectedPathname)
114+
req.url = url.href.slice(url.origin.length)
111115
}
112116

113117
serve(req, res, next)
@@ -121,16 +125,17 @@ export function serveRawFsMiddleware(
121125

122126
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
123127
return function viteServeRawFsMiddleware(req, res, next) {
124-
let url = decodeURIComponent(req.url!)
128+
const url = new URL(req.url!, 'http://example.com')
125129
// In some cases (e.g. linked monorepos) files outside of root will
126130
// reference assets that are also out of served root. In such cases
127131
// the paths are rewritten to `/@fs/` prefixed paths and must be served by
128132
// searching based from fs root.
129-
if (url.startsWith(FS_PREFIX)) {
133+
if (url.pathname.startsWith(FS_PREFIX)) {
134+
const pathname = decodeURIComponent(url.pathname)
130135
// restrict files outside of `fs.allow`
131136
if (
132137
!ensureServingAccess(
133-
slash(path.resolve(fsPathFromId(url))),
138+
slash(path.resolve(fsPathFromId(pathname))),
134139
server,
135140
res,
136141
next
@@ -139,10 +144,11 @@ export function serveRawFsMiddleware(
139144
return
140145
}
141146

142-
url = url.slice(FS_PREFIX.length)
143-
if (isWindows) url = url.replace(/^[A-Z]:/i, '')
147+
let newPathname = pathname.slice(FS_PREFIX.length)
148+
if (isWindows) newPathname = newPathname.replace(/^[A-Z]:/i, '')
144149

145-
req.url = encodeURIComponent(url)
150+
url.pathname = encodeURIComponent(newPathname)
151+
req.url = url.href.slice(url.origin.length)
146152
serveFromRoot(req, res, next)
147153
} else {
148154
next()

0 commit comments

Comments
 (0)