Skip to content

Commit 7dd7d4c

Browse files
committed
add test for base fs-serve
1 parent 4fcfcf4 commit 7dd7d4c

File tree

6 files changed

+228
-21
lines changed

6 files changed

+228
-21
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import fetch from 'node-fetch'
2+
import { beforeAll, describe, expect, test } from 'vitest'
3+
import testJSON from '../../safe.json'
4+
import { isServe, page, viteTestUrl } from '~utils'
5+
6+
const stringified = JSON.stringify(testJSON)
7+
8+
describe.runIf(isServe)('main', () => {
9+
beforeAll(async () => {
10+
const srcPrefix = viteTestUrl.endsWith('/') ? '' : '/'
11+
await page.goto(viteTestUrl + srcPrefix + 'src/')
12+
})
13+
14+
test('default import', async () => {
15+
expect(await page.textContent('.full')).toBe(stringified)
16+
})
17+
18+
test('named import', async () => {
19+
expect(await page.textContent('.named')).toBe(testJSON.msg)
20+
})
21+
22+
test('safe fetch', async () => {
23+
expect(await page.textContent('.safe-fetch')).toMatch('KEY=safe')
24+
expect(await page.textContent('.safe-fetch-status')).toBe('200')
25+
})
26+
27+
test('safe fetch with query', async () => {
28+
expect(await page.textContent('.safe-fetch-query')).toMatch('KEY=safe')
29+
expect(await page.textContent('.safe-fetch-query-status')).toBe('200')
30+
})
31+
32+
test('safe fetch with special characters', async () => {
33+
expect(
34+
await page.textContent('.safe-fetch-subdir-special-characters'),
35+
).toMatch('KEY=safe')
36+
expect(
37+
await page.textContent('.safe-fetch-subdir-special-characters-status'),
38+
).toBe('200')
39+
})
40+
41+
test('unsafe fetch', async () => {
42+
expect(await page.textContent('.unsafe-fetch')).toMatch('403 Restricted')
43+
expect(await page.textContent('.unsafe-fetch-status')).toBe('403')
44+
})
45+
46+
test('unsafe fetch with special characters (#8498)', async () => {
47+
expect(await page.textContent('.unsafe-fetch-8498')).toBe('')
48+
expect(await page.textContent('.unsafe-fetch-8498-status')).toBe('404')
49+
})
50+
51+
test('unsafe fetch with special characters 2 (#8498)', async () => {
52+
expect(await page.textContent('.unsafe-fetch-8498-2')).toBe('')
53+
expect(await page.textContent('.unsafe-fetch-8498-2-status')).toBe('404')
54+
})
55+
56+
test('safe fs fetch', async () => {
57+
expect(await page.textContent('.safe-fs-fetch')).toBe(stringified)
58+
expect(await page.textContent('.safe-fs-fetch-status')).toBe('200')
59+
})
60+
61+
test('safe fs fetch', async () => {
62+
expect(await page.textContent('.safe-fs-fetch-query')).toBe(stringified)
63+
expect(await page.textContent('.safe-fs-fetch-query-status')).toBe('200')
64+
})
65+
66+
test('safe fs fetch with special characters', async () => {
67+
expect(await page.textContent('.safe-fs-fetch-special-characters')).toBe(
68+
stringified,
69+
)
70+
expect(
71+
await page.textContent('.safe-fs-fetch-special-characters-status'),
72+
).toBe('200')
73+
})
74+
75+
test('unsafe fs fetch', async () => {
76+
expect(await page.textContent('.unsafe-fs-fetch')).toBe('')
77+
expect(await page.textContent('.unsafe-fs-fetch-status')).toBe('403')
78+
})
79+
80+
test('unsafe fs fetch with special characters (#8498)', async () => {
81+
expect(await page.textContent('.unsafe-fs-fetch-8498')).toBe('')
82+
expect(await page.textContent('.unsafe-fs-fetch-8498-status')).toBe('404')
83+
})
84+
85+
test('unsafe fs fetch with special characters 2 (#8498)', async () => {
86+
expect(await page.textContent('.unsafe-fs-fetch-8498-2')).toBe('')
87+
expect(await page.textContent('.unsafe-fs-fetch-8498-2-status')).toBe('404')
88+
})
89+
90+
test('nested entry', async () => {
91+
expect(await page.textContent('.nested-entry')).toBe('foobar')
92+
})
93+
94+
test('denied', async () => {
95+
expect(await page.textContent('.unsafe-dotenv')).toBe('404')
96+
})
97+
})
98+
99+
describe('fetch', () => {
100+
test('serve with configured headers', async () => {
101+
const res = await fetch(viteTestUrl + '/src/')
102+
expect(res.headers.get('x-served-by')).toBe('vite')
103+
})
104+
})
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import path from 'node:path'
2+
import { mergeConfig } from 'vite'
3+
import config from '../../root/vite.config-base'
4+
import { isBuild, page, rootDir, setViteUrl, viteTestUrl } from '~utils'
5+
6+
export async function serve(): Promise<{ close(): Promise<void> }> {
7+
const { createServer } = await import('vite')
8+
process.env.VITE_INLINE = 'inline-serve'
9+
10+
const options = {
11+
...config,
12+
root: rootDir,
13+
logLevel: 'silent',
14+
build: {
15+
target: 'esnext',
16+
},
17+
server: {
18+
watch: {
19+
usePolling: true,
20+
interval: 100,
21+
},
22+
host: true,
23+
fs: {
24+
strict: !isBuild,
25+
},
26+
},
27+
}
28+
29+
const rewriteTestRootOptions = {
30+
build: {
31+
rollupOptions: {
32+
input: {
33+
main: path.resolve(rootDir, 'src/index.html'),
34+
},
35+
},
36+
},
37+
server: {
38+
fs: {
39+
strict: true,
40+
allow: [path.resolve(rootDir, 'src')],
41+
},
42+
},
43+
define: {
44+
ROOT: JSON.stringify(path.dirname(rootDir).replace(/\\/g, '/')),
45+
},
46+
}
47+
48+
const viteServer = await (
49+
await createServer(mergeConfig(options, rewriteTestRootOptions))
50+
).listen()
51+
52+
// use resolved port/base from server
53+
const devBase = viteServer.config.base === '/' ? '' : viteServer.config.base
54+
55+
setViteUrl(`http://localhost:${viteServer.config.server.port}${devBase}`)
56+
await page.goto(viteTestUrl)
57+
58+
return viteServer
59+
}

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ const stringified = JSON.stringify(testJSON)
77

88
describe.runIf(isServe)('main', () => {
99
beforeAll(async () => {
10-
await page.goto(viteTestUrl + '/src/')
10+
const srcPrefix = viteTestUrl.endsWith('/') ? '' : '/'
11+
await page.goto(viteTestUrl + srcPrefix + 'src/')
1112
})
1213

1314
test('default import', async () => {
@@ -66,7 +67,9 @@ describe.runIf(isServe)('main', () => {
6667
expect(await page.textContent('.safe-fs-fetch-special-characters')).toBe(
6768
stringified,
6869
)
69-
expect(await page.textContent('.safe-fs-fetch-status')).toBe('200')
70+
expect(
71+
await page.textContent('.safe-fs-fetch-special-characters-status'),
72+
).toBe('200')
7073
})
7174

7275
test('unsafe fs fetch', async () => {
@@ -88,10 +91,6 @@ describe.runIf(isServe)('main', () => {
8891
expect(await page.textContent('.nested-entry')).toBe('foobar')
8992
})
9093

91-
test('nested entry', async () => {
92-
expect(await page.textContent('.nested-entry')).toBe('foobar')
93-
})
94-
9594
test('denied', async () => {
9695
expect(await page.textContent('.unsafe-dotenv')).toBe('404')
9796
})

‎playground/fs-serve/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"dev": "vite root",
88
"build": "vite build root",
99
"debug": "node --inspect-brk ../../packages/vite/bin/vite",
10-
"preview": "vite preview root"
10+
"preview": "vite preview root",
11+
"dev:base": "vite root --config ./root/vite.config-base.js",
12+
"build:base": "vite build root --config ./root/vite.config-base.js",
13+
"preview:base": "vite preview root --config ./root/vite.config-base.js"
1114
}
1215
}

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

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ <h2>Denied</h2>
5353
text('.full', JSON.stringify(json))
5454
text('.named', msg)
5555

56+
const base = typeof BASE !== 'undefined' ? BASE : ''
57+
5658
// inside allowed dir, safe fetch
57-
fetch('/src/safe.txt')
59+
fetch(base + '/src/safe.txt')
5860
.then((r) => {
5961
text('.safe-fetch-status', r.status)
6062
return r.text()
@@ -64,7 +66,7 @@ <h2>Denied</h2>
6466
})
6567

6668
// inside allowed dir with query, safe fetch
67-
fetch('/src/safe.txt?query')
69+
fetch(base + '/src/safe.txt?query')
6870
.then((r) => {
6971
text('.safe-fetch-query-status', r.status)
7072
return r.text()
@@ -74,7 +76,7 @@ <h2>Denied</h2>
7476
})
7577

7678
// inside allowed dir, safe fetch
77-
fetch('/src/subdir/safe.txt')
79+
fetch(base + '/src/subdir/safe.txt')
7880
.then((r) => {
7981
text('.safe-fetch-subdir-status', r.status)
8082
return r.text()
@@ -84,7 +86,7 @@ <h2>Denied</h2>
8486
})
8587

8688
// inside allowed dir, with special characters, safe fetch
87-
fetch('/src/special%20characters%20%C3%A5%C3%A4%C3%B6/safe.txt')
89+
fetch(base + '/src/special%20characters%20%C3%A5%C3%A4%C3%B6/safe.txt')
8890
.then((r) => {
8991
text('.safe-fetch-subdir-special-characters-status', r.status)
9092
return r.text()
@@ -94,7 +96,7 @@ <h2>Denied</h2>
9496
})
9597

9698
// outside of allowed dir, treated as unsafe
97-
fetch('/unsafe.txt')
99+
fetch(base + '/unsafe.txt')
98100
.then((r) => {
99101
text('.unsafe-fetch-status', r.status)
100102
return r.text()
@@ -107,7 +109,7 @@ <h2>Denied</h2>
107109
})
108110

109111
// outside of allowed dir with special characters #8498
110-
fetch('/src/%2e%2e%2funsafe%2etxt')
112+
fetch(base + '/src/%2e%2e%2funsafe%2etxt')
111113
.then((r) => {
112114
text('.unsafe-fetch-8498-status', r.status)
113115
return r.text()
@@ -120,7 +122,7 @@ <h2>Denied</h2>
120122
})
121123

122124
// outside of allowed dir with special characters 2 #8498
123-
fetch('/src/%252e%252e%252funsafe%252etxt')
125+
fetch(base + '/src/%252e%252e%252funsafe%252etxt')
124126
.then((r) => {
125127
text('.unsafe-fetch-8498-2-status', r.status)
126128
return r.text()
@@ -133,7 +135,7 @@ <h2>Denied</h2>
133135
})
134136

135137
// imported before, should be treated as safe
136-
fetch('/@fs/' + ROOT + '/safe.json')
138+
fetch(base + '/@fs/' + ROOT + '/safe.json')
137139
.then((r) => {
138140
text('.safe-fs-fetch-status', r.status)
139141
return r.json()
@@ -143,7 +145,7 @@ <h2>Denied</h2>
143145
})
144146

145147
// imported before with query, should be treated as safe
146-
fetch('/@fs/' + ROOT + '/safe.json?query')
148+
fetch(base + '/@fs/' + ROOT + '/safe.json?query')
147149
.then((r) => {
148150
text('.safe-fs-fetch-query-status', r.status)
149151
return r.json()
@@ -153,7 +155,7 @@ <h2>Denied</h2>
153155
})
154156

155157
// not imported before, outside of root, treated as unsafe
156-
fetch('/@fs/' + ROOT + '/unsafe.json')
158+
fetch(base + '/@fs/' + ROOT + '/unsafe.json')
157159
.then((r) => {
158160
text('.unsafe-fs-fetch-status', r.status)
159161
return r.json()
@@ -166,7 +168,7 @@ <h2>Denied</h2>
166168
})
167169

168170
// outside root with special characters #8498
169-
fetch('/@fs/' + ROOT + '/root/src/%2e%2e%2f%2e%2e%2funsafe%2ejson')
171+
fetch(base + '/@fs/' + ROOT + '/root/src/%2e%2e%2f%2e%2e%2funsafe%2ejson')
170172
.then((r) => {
171173
text('.unsafe-fs-fetch-8498-status', r.status)
172174
return r.json()
@@ -177,7 +179,10 @@ <h2>Denied</h2>
177179

178180
// outside root with special characters 2 #8498
179181
fetch(
180-
'/@fs/' + ROOT + '/root/src/%252e%252e%252f%252e%252e%252funsafe%252ejson',
182+
base +
183+
'/@fs/' +
184+
ROOT +
185+
'/root/src/%252e%252e%252f%252e%252e%252funsafe%252ejson',
181186
)
182187
.then((r) => {
183188
text('.unsafe-fs-fetch-8498-2-status', r.status)
@@ -189,7 +194,8 @@ <h2>Denied</h2>
189194

190195
// not imported before, inside root with special characters, treated as safe
191196
fetch(
192-
'/@fs/' +
197+
base +
198+
'/@fs/' +
193199
ROOT +
194200
'/root/src/special%20characters%20%C3%A5%C3%A4%C3%B6/safe.json',
195201
)
@@ -202,7 +208,7 @@ <h2>Denied</h2>
202208
})
203209

204210
// .env, denied by default
205-
fetch('/@fs/' + ROOT + '/root/.env')
211+
fetch(base + '/@fs/' + ROOT + '/root/.env')
206212
.then((r) => {
207213
text('.unsafe-dotenv', r.status)
208214
})
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import path from 'node:path'
2+
import { defineConfig } from 'vite'
3+
4+
const BASE = '/base'
5+
6+
export default defineConfig({
7+
base: BASE,
8+
build: {
9+
rollupOptions: {
10+
input: {
11+
main: path.resolve(__dirname, 'src/index.html'),
12+
},
13+
},
14+
},
15+
server: {
16+
fs: {
17+
strict: true,
18+
allow: [path.resolve(__dirname, 'src')],
19+
},
20+
hmr: {
21+
overlay: false,
22+
},
23+
headers: {
24+
'x-served-by': 'vite',
25+
},
26+
},
27+
preview: {
28+
headers: {
29+
'x-served-by': 'vite',
30+
},
31+
},
32+
define: {
33+
ROOT: JSON.stringify(path.dirname(__dirname).replace(/\\/g, '/')),
34+
BASE: JSON.stringify(BASE),
35+
},
36+
})

0 commit comments

Comments
 (0)