Skip to content

Commit

Permalink
fix: fs-serve import graph awareness
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Jun 13, 2021
1 parent 9703bcd commit 95dbcf4
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 44 deletions.
5 changes: 5 additions & 0 deletions packages/vite/src/node/plugins/importAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,11 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
)
let url = normalizedUrl

// record as safe modules
server?.moduleGraph.safeModulesPath.add(
cleanUrl(url).slice(4 /* '/@fs'.length */)
)

// rewrite
if (url !== specifier) {
// for optimized cjs deps, support named imports by rewriting named
Expand Down
15 changes: 13 additions & 2 deletions packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { createMissingImporterRegisterFn } from '../optimizer/registerMissing'
import { printServerUrls } from '../logger'
import { resolveHostname } from '../utils'
import { searchForWorkspaceRoot } from './searchRoot'
import { CLIENT_DIR } from '../constants'

export interface ServerOptions {
host?: string | boolean
Expand Down Expand Up @@ -151,6 +152,8 @@ export interface FileSystemServeOptions {
* @expiremental
*/
root?: string

dirs?: string[]
}

/**
Expand Down Expand Up @@ -484,7 +487,7 @@ export async function createServer(
middlewares.use(transformMiddleware(server))

// serve static files
middlewares.use(serveRawFsMiddleware(config))
middlewares.use(serveRawFsMiddleware(server, config))
middlewares.use(serveStaticMiddleware(root, config))

// spa fallback
Expand Down Expand Up @@ -703,11 +706,19 @@ export function resolveServerOptions(
const fsServeRoot = normalizePath(
path.resolve(root, server.fsServe?.root || searchForWorkspaceRoot(root))
)
const fsServeDirs = (server.fsServe?.dirs || []).map((i) =>
normalizePath(path.resolve(root, i))
)

fsServeDirs.push(CLIENT_DIR)
fsServeDirs.push(fsServeRoot)

// TODO: make strict by default
const fsServeStrict = server.fsServe?.strict ?? false
server.fsServe = {
root: fsServeRoot,
strict: fsServeStrict
strict: fsServeStrict,
dirs: fsServeDirs
}
return server as ResolvedServerOptions
}
76 changes: 37 additions & 39 deletions packages/vite/src/node/server/middlewares/static.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import path from 'path'
import sirv, { Options } from 'sirv'
import { Connect } from 'types/connect'
import { FileSystemServeOptions } from '..'
import { normalizePath, ResolvedConfig } from '../..'
import { normalizePath, ResolvedConfig, ViteDevServer } from '../..'
import { FS_PREFIX } from '../../constants'
import { Logger } from '../../logger'
import { cleanUrl, fsPathFromId, isImportRequest, isWindows } from '../../utils'
import { AccessRestrictedError } from './error'

Expand Down Expand Up @@ -77,6 +75,7 @@ export function serveStaticMiddleware(
}

export function serveRawFsMiddleware(
server: ViteDevServer,
config: ResolvedConfig
): Connect.NextHandleFunction {
const serveFromRoot = sirv('/', sirvOptions)
Expand All @@ -90,11 +89,7 @@ export function serveRawFsMiddleware(
// searching based from fs root.
if (url.startsWith(FS_PREFIX)) {
// restrict files outside of `fsServe.root`
ensureServingAccess(
path.resolve(fsPathFromId(url)),
config.server.fsServe,
config.logger
)
ensureServingAccess(path.resolve(fsPathFromId(url)), server)

url = url.slice(FS_PREFIX.length)
if (isWindows) url = url.replace(/^[A-Z]:/i, '')
Expand All @@ -107,40 +102,43 @@ export function serveRawFsMiddleware(
}
}

export function isFileAccessAllowed(
export function isFileServingAllowed(
url: string,
{ root, strict }: Required<FileSystemServeOptions>
server: ViteDevServer
): boolean {
return !strict || normalizePath(url).startsWith(root + path.posix.sep)
if (!server.config.server.fsServe.strict) return true

const file = cleanUrl(url)

if (server.moduleGraph.safeModulesPath.has(file)) {
return true
}

const normalizedUrl = normalizePath(file)
return server.config.server.fsServe.dirs.some((i) =>
normalizedUrl.startsWith(i + path.posix.sep)
)
}

export function ensureServingAccess(
url: string,
serveOptions: Required<FileSystemServeOptions>,
logger: Logger
): void {
const { strict, root } = serveOptions
// TODO: early return, should remove once we polished the restriction logic
if (!strict) return

if (!isFileAccessAllowed(url, serveOptions)) {
const normalizedUrl = normalizePath(url)
if (strict) {
throw new AccessRestrictedError(
`The request url "${normalizedUrl}" is outside of vite dev server root "${root}".
For security concerns, accessing files outside of workspace root is restricted since Vite v2.3.x.
Refer to docs https://vitejs.dev/config/#server-fsserve-root for configurations and more details.`,
normalizedUrl,
root
)
} else {
// TODO: warn for potential unrestricted access
logger.warnOnce(
`For security concerns, accessing files outside of workspace root will ` +
`be restricted by default in the future version of Vite. ` +
`Refer to [] for more`
)
logger.warnOnce(`Unrestricted file system access to "${normalizedUrl}"`)
}
export function ensureServingAccess(url: string, server: ViteDevServer): void {
if (isFileServingAllowed(url, server)) return

const { strict, root } = server.config.server.fsServe

if (strict) {
throw new AccessRestrictedError(
`The request url "${url}" is outside of vite dev server root "${root}".
For security concerns, accessing files outside of workspace root is restricted since Vite v2.3.x.
Refer to docs https://vitejs.dev/config/#server-fsserve-root for configurations and more details.`,
url,
root
)
} else {
server.config.logger.warnOnce(`Unrestricted file system access to "${url}"`)
server.config.logger.warnOnce(
`For security concerns, accessing files outside of workspace root will ` +
`be restricted by default in the future version of Vite. ` +
`Refer to [] for more`
)
}
}
1 change: 1 addition & 0 deletions packages/vite/src/node/server/moduleGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class ModuleGraph {
idToModuleMap = new Map<string, ModuleNode>()
// a single file may corresponds to multiple modules with different queries
fileToModulesMap = new Map<string, Set<ModuleNode>>()
safeModulesPath = new Set<string>()
container: PluginContainer

constructor(container: PluginContainer) {
Expand Down
8 changes: 5 additions & 3 deletions packages/vite/src/node/server/transformRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { checkPublicFile } from '../plugins/asset'
import { ssrTransform } from '../ssr/ssrTransform'
import { injectSourcesContent } from './sourcemap'
import { isFileAccessAllowed } from './middlewares/static'
import { isFileServingAllowed } from './middlewares/static'

const debugLoad = createDebugger('vite:load')
const debugTransform = createDebugger('vite:transform')
Expand All @@ -37,9 +37,11 @@ export interface TransformOptions {

export async function transformRequest(
url: string,
{ config, pluginContainer, moduleGraph, watcher }: ViteDevServer,
server: ViteDevServer,
options: TransformOptions = {}
): Promise<TransformResult | null> {
const { config, pluginContainer, moduleGraph, watcher } = server

url = removeTimestampQuery(url)
const { root, logger } = config
const prettyUrl = isDebug ? prettifyUrl(url, root) : ''
Expand Down Expand Up @@ -75,7 +77,7 @@ export async function transformRequest(
// as string
// only try the fallback if access is allowed, skip for out of root url
// like /service-worker.js or /api/users
if (options.ssr || isFileAccessAllowed(file, config.server.fsServe)) {
if (options.ssr || isFileServingAllowed(file, server)) {
try {
code = await fs.readFile(file, 'utf-8')
isDebug && debugLoad(`${timeFrom(loadStart)} [fs] ${prettyUrl}`)
Expand Down

0 comments on commit 95dbcf4

Please sign in to comment.