/
static.ts
163 lines (141 loc) · 5.05 KB
/
static.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import path from 'path'
import sirv, { Options } from 'sirv'
import { Connect } from 'types/connect'
import { normalizePath, ViteDevServer } from '../..'
import { FS_PREFIX } from '../../constants'
import {
cleanUrl,
ensureLeadingSlash,
fsPathFromId,
isImportRequest,
isInternalRequest,
isWindows,
slash
} from '../../utils'
import { AccessRestrictedError } from './error'
const sirvOptions: Options = {
dev: true,
etag: true,
extensions: [],
setHeaders(res, pathname) {
// Matches js, jsx, ts, tsx.
// The reason this is done, is that the .ts file extension is reserved
// for the MIME type video/mp2t. In almost all cases, we can expect
// these files to be TypeScript files, and for Vite to serve them with
// this Content-Type.
if (/\.[tj]sx?$/.test(pathname)) {
res.setHeader('Content-Type', 'application/javascript')
}
}
}
export function servePublicMiddleware(dir: string): Connect.NextHandleFunction {
const serve = sirv(dir, sirvOptions)
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
return function viteServePublicMiddleware(req, res, next) {
// skip import request and internal requests `/@fs/ /@vite-client` etc...
if (isImportRequest(req.url!) || isInternalRequest(req.url!)) {
return next()
}
serve(req, res, next)
}
}
export function serveStaticMiddleware(
dir: string,
server: ViteDevServer
): Connect.NextHandleFunction {
const serve = sirv(dir, sirvOptions)
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
return async function viteServeStaticMiddleware(req, res, next) {
// only serve the file if it's not an html request
// so that html requests can fallthrough to our html middleware for
// special processing
// also skip internal requests `/@fs/ /@vite-client` etc...
if (
path.extname(cleanUrl(req.url!)) === '.html' ||
isInternalRequest(req.url!)
) {
return next()
}
const url = decodeURI(req.url!)
// apply aliases to static requests as well
let redirected: string | undefined
for (const { find, replacement } of server.config.resolve.alias) {
const matches =
typeof find === 'string' ? url.startsWith(find) : find.test(url)
if (matches) {
redirected = url.replace(find, replacement)
break
}
}
if (redirected) {
// dir is pre-normalized to posix style
if (redirected.startsWith(dir)) {
redirected = redirected.slice(dir.length)
}
}
const resolvedUrl = redirected || url
const fileUrl = path.resolve(dir, resolvedUrl.startsWith('/') ? resolvedUrl.slice(1) : resolvedUrl)
// TODO: should use ensureServingAccess(fileUrl, server)
if (!isFileServingAllowed(fileUrl, server)) {
return next()
}
if (redirected) {
req.url = redirected
}
serve(req, res, next)
}
}
export function serveRawFsMiddleware(
server: ViteDevServer
): Connect.NextHandleFunction {
const serveFromRoot = sirv('/', sirvOptions)
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
return function viteServeRawFsMiddleware(req, res, next) {
let url = 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
// searching based from fs root.
if (url.startsWith(FS_PREFIX)) {
// restrict files outside of `fs.allow`
ensureServingAccess(slash(path.resolve(fsPathFromId(url))), server)
url = url.slice(FS_PREFIX.length)
if (isWindows) url = url.replace(/^[A-Z]:/i, '')
req.url = url
serveFromRoot(req, res, next)
} else {
next()
}
}
}
export function isFileServingAllowed(
url: string,
server: ViteDevServer
): boolean {
// explicitly disabled
if (server.config.server.fs.strict === false) return true
const file = ensureLeadingSlash(normalizePath(cleanUrl(url)))
if (server.moduleGraph.safeModulesPath.has(file)) return true
if (server.config.server.fs.allow.some((i) => file.startsWith(i + '/')))
return true
if (!server.config.server.fs.strict) {
server.config.logger.warnOnce(`Unrestricted file system access to "${url}"`)
server.config.logger.warnOnce(
`For security concerns, accessing files outside of serving allow list will ` +
`be restricted by default in the future version of Vite. ` +
`Refer to https://vitejs.dev/config/#server-fs-allow for more details.`
)
return true
}
return false
}
export function ensureServingAccess(url: string, server: ViteDevServer): void {
if (!isFileServingAllowed(url, server)) {
const allow = server.config.server.fs.allow
throw new AccessRestrictedError(
`The request url "${url}" is outside of Vite serving allow list:
${allow.map((i) => `- ${i}`).join('\n')}
Refer to docs https://vitejs.dev/config/#server-fs-allow for configurations and more details.`
)
}
}