Skip to content

Commit 4be5270

Browse files
authored
fix(dev): denied requests overly (vitejs#20410)
1 parent f6aa04a commit 4be5270

File tree

5 files changed

+27
-52
lines changed

5 files changed

+27
-52
lines changed

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

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
isImportRequest,
1414
isInternalRequest,
1515
isParentDirectory,
16-
isSameFileUri,
16+
isSameFilePath,
1717
normalizePath,
1818
removeLeadingSlash,
1919
urlRE,
@@ -262,10 +262,22 @@ export function isFileServingAllowed(
262262
return isFileLoadingAllowed(config, filePath)
263263
}
264264

265-
function isUriInFilePath(uri: string, filePath: string) {
266-
return isSameFileUri(uri, filePath) || isParentDirectory(uri, filePath)
265+
/**
266+
* Warning: parameters are not validated, only works with normalized absolute paths
267+
*
268+
* @param targetPath - normalized absolute path
269+
* @param filePath - normalized absolute path
270+
*/
271+
function isFileInTargetPath(targetPath: string, filePath: string) {
272+
return (
273+
isSameFilePath(targetPath, filePath) ||
274+
isParentDirectory(targetPath, filePath)
275+
)
267276
}
268277

278+
/**
279+
* Warning: parameters are not validated, only works with normalized absolute paths
280+
*/
269281
export function isFileLoadingAllowed(
270282
config: ResolvedConfig,
271283
filePath: string,
@@ -278,7 +290,7 @@ export function isFileLoadingAllowed(
278290

279291
if (config.safeModulePaths.has(filePath)) return true
280292

281-
if (fs.allow.some((uri) => isUriInFilePath(uri, filePath))) return true
293+
if (fs.allow.some((uri) => isFileInTargetPath(uri, filePath))) return true
282294

283295
return false
284296
}
@@ -298,27 +310,12 @@ export function checkLoadingAccess(
298310
return 'fallback'
299311
}
300312

301-
export function checkServingAccess(
302-
url: string,
303-
server: ViteDevServer,
304-
): 'allowed' | 'denied' | 'fallback' {
305-
if (isFileServingAllowed(url, server)) {
306-
return 'allowed'
307-
}
308-
if (isFileReadable(cleanUrl(url))) {
309-
return 'denied'
310-
}
311-
// if the file doesn't exist, we shouldn't restrict this path as it can
312-
// be an API call. Middlewares would issue a 404 if the file isn't handled
313-
return 'fallback'
314-
}
315-
316313
export function respondWithAccessDenied(
317-
url: string,
314+
id: string,
318315
server: ViteDevServer,
319316
res: ServerResponse,
320317
): void {
321-
const urlMessage = `The request url "${url}" is outside of Vite serving allow list.`
318+
const urlMessage = `The request id "${id}" is outside of Vite serving allow list.`
322319
const hintMessage = `
323320
${server.config.server.fs.allow.map((i) => `- ${i}`).join('\n')}
324321

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

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,11 @@ import {
3434
ERR_OUTDATED_OPTIMIZED_DEP,
3535
NULL_BYTE_PLACEHOLDER,
3636
} from '../../../shared/constants'
37-
import { checkServingAccess, respondWithAccessDenied } from './static'
37+
import { checkLoadingAccess, respondWithAccessDenied } from './static'
3838

3939
const debugCache = createDebugger('vite:cache')
4040

4141
const knownIgnoreList = new Set(['/', '/favicon.ico'])
42-
const trailingQuerySeparatorsRE = /[?&]+$/
4342

4443
// TODO: consolidate this regex pattern with the url, raw, and inline checks in plugins
4544
const urlRE = /[?&]url\b/
@@ -48,20 +47,15 @@ const inlineRE = /[?&]inline\b/
4847
const svgRE = /\.svg\b/
4948

5049
function deniedServingAccessForTransform(
51-
url: string,
50+
id: string,
5251
server: ViteDevServer,
5352
res: ServerResponse,
5453
next: Connect.NextFunction,
5554
) {
56-
if (
57-
rawRE.test(url) ||
58-
urlRE.test(url) ||
59-
inlineRE.test(url) ||
60-
svgRE.test(url)
61-
) {
62-
const servingAccessResult = checkServingAccess(url, server)
55+
if (rawRE.test(id) || urlRE.test(id) || inlineRE.test(id) || svgRE.test(id)) {
56+
const servingAccessResult = checkLoadingAccess(server.config, id)
6357
if (servingAccessResult === 'denied') {
64-
respondWithAccessDenied(url, server, res)
58+
respondWithAccessDenied(id, server, res)
6559
return true
6660
}
6761
if (servingAccessResult === 'fallback') {
@@ -207,22 +201,6 @@ export function transformMiddleware(
207201
warnAboutExplicitPublicPathInUrl(url)
208202
}
209203

210-
const urlWithoutTrailingQuerySeparators = url.replace(
211-
trailingQuerySeparatorsRE,
212-
'',
213-
)
214-
if (
215-
!url.startsWith('/@id/\0') &&
216-
deniedServingAccessForTransform(
217-
urlWithoutTrailingQuerySeparators,
218-
server,
219-
res,
220-
next,
221-
)
222-
) {
223-
return
224-
}
225-
226204
if (
227205
req.headers['sec-fetch-dest'] === 'script' ||
228206
isJSRequest(url) ||

packages/vite/src/node/server/transformRequest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from '../utils'
2222
import { ssrTransform } from '../ssr/ssrTransform'
2323
import { checkPublicFile } from '../publicDir'
24-
import { cleanUrl, unwrapId } from '../../shared/utils'
24+
import { cleanUrl, slash, unwrapId } from '../../shared/utils'
2525
import {
2626
applySourcemapIgnoreList,
2727
extractSourcemapFromFile,
@@ -268,7 +268,7 @@ async function loadAndTransform(
268268
// like /service-worker.js or /api/users
269269
if (
270270
environment.config.consumer === 'server' ||
271-
isFileLoadingAllowed(environment.getTopLevelConfig(), file)
271+
isFileLoadingAllowed(environment.getTopLevelConfig(), slash(file))
272272
) {
273273
try {
274274
code = await fsp.readFile(file, 'utf-8')

packages/vite/src/node/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ export function isParentDirectory(dir: string, file: string): boolean {
285285
* @param file2 - normalized absolute path
286286
* @returns true if both files url are identical
287287
*/
288-
export function isSameFileUri(file1: string, file2: string): boolean {
288+
export function isSameFilePath(file1: string, file2: string): boolean {
289289
return (
290290
file1 === file2 ||
291291
(isCaseInsensitiveFS && file1.toLowerCase() === file2.toLowerCase())

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ describe.runIf(isServe)('main', () => {
208208
.poll(() =>
209209
page.textContent('.unsafe-fs-fetch-relative-path-after-query-status'),
210210
)
211-
.toBe('403')
211+
.toBe('404')
212212
})
213213

214214
test('nested entry', async () => {

0 commit comments

Comments
 (0)