Skip to content

Commit 02cffa9

Browse files
authored
fix: reduce Windows 8.3-short-name detection false-positives (#23066)
1 parent ba958bd commit 02cffa9

2 files changed

Lines changed: 60 additions & 4 deletions

File tree

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, test } from 'vitest'
2-
import { isFileInTargetPath } from '../static'
2+
import { isFileInTargetPath, looksLikeWindowsShortNamePath } from '../static'
33

44
describe('isFileInTargetPath', () => {
55
const cases = {
@@ -27,3 +27,38 @@ describe('isFileInTargetPath', () => {
2727
}
2828
}
2929
})
30+
31+
describe('looksLikeWindowsShortNamePath', () => {
32+
const shortNamePaths = [
33+
// classic 8.3 short names
34+
'C:/PROGRA~1/x',
35+
'C:/PROGRA~1',
36+
'C:/LONGFI~1.TXT',
37+
'C:/MICROS~2/foo',
38+
// short-name-looking directory ancestor, not just the basename
39+
'C:/foo/DOCUME~1/bar.js',
40+
]
41+
const legitimateTildePaths = [
42+
// real-world case from the reported issue: `~` not followed by a digit
43+
'C:/project/dist/0~rslib-runtime.js',
44+
// ancestor directory containing a tilde that isn't short-name shaped
45+
'C:/Users/foo~bar/project/index.js',
46+
// tilde-prefixed name with no short-name-style prefix/digit suffix
47+
'C:/Users/foo/~backup/index.js',
48+
// prefix longer than the 6 characters a short name can have
49+
'C:/project/confirmations~2/index.js',
50+
// no tilde at all
51+
'C:/Users/foo/project/index.js',
52+
]
53+
54+
for (const filePath of shortNamePaths) {
55+
test(`looksLikeWindowsShortNamePath("${filePath}") is true`, () => {
56+
expect(looksLikeWindowsShortNamePath(filePath)).toBe(true)
57+
})
58+
}
59+
for (const filePath of legitimateTildePaths) {
60+
test(`looksLikeWindowsShortNamePath("${filePath}") is false`, () => {
61+
expect(looksLikeWindowsShortNamePath(filePath)).toBe(false)
62+
})
63+
}
64+
})

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,24 @@ export function isFileInTargetPath(
287287
}
288288

289289
const windowsDriveRE = /^[A-Z]:/i
290+
// A Windows 8.3 "short name" segment looks like `NAME~1` or `NAME~1.EXT`: at
291+
// most 6 non-`~`/`.` characters, a `~`, then digits, within a single path
292+
// segment. Matching only this shape (rather than any `~`) still blocks the
293+
// short-name aliasing bypass while allowing filenames that merely contain a
294+
// tilde, e.g. `0~rslib-runtime.js`.
295+
const windowsShortNameSegmentRE = /^[^~.]{1,6}~\d+(?:\.[^~.]{0,3})?$/
296+
297+
/**
298+
* Warning: parameters are not validated, only works with normalized absolute paths
299+
*/
300+
export function looksLikeWindowsShortNamePath(filePath: string): boolean {
301+
return (
302+
filePath.includes('~') &&
303+
filePath
304+
.split('/')
305+
.some((segment) => windowsShortNameSegmentRE.test(segment))
306+
)
307+
}
290308

291309
/**
292310
* Warning: parameters are not validated, only works with normalized absolute paths
@@ -299,9 +317,12 @@ export function isFileLoadingAllowed(
299317

300318
if (!fs.strict) return true
301319

302-
if (isWindows && filePath.includes('~')) {
303-
// `~` is used for Windows 8.3 short names, which can be used to bypass the check.
304-
// While is it valid to have files with `~` in the path, we disallow it to be safe.
320+
if (isWindows && looksLikeWindowsShortNamePath(filePath)) {
321+
// Windows 8.3 short names (e.g. `PROGRA~1`) can alias a different long
322+
// path and can be used to bypass the check.
323+
// While it is valid to have files named similar to automatically generated
324+
// short names, it is unlikely that a user would create them, so we
325+
// disallow them to be safe.
305326
return false
306327
}
307328

0 commit comments

Comments
 (0)