Skip to content

Commit

Permalink
fix: disable fsServe restrictions by default (#3377)
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed May 12, 2021
1 parent 42b35ac commit 5433a65
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 14 deletions.
24 changes: 23 additions & 1 deletion docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,17 +430,39 @@ export default async ({ command, mode }) => {

File system watcher options to pass on to [chokidar](https://github.com/paulmillr/chokidar#api).

### server.fsServe.strict

- **Experimental**
- **Type:** `boolean`
- **Default:** `false` (will change to `true` in future versions)

Restrict serving files outside of workspace root.

### server.fsServe.root

- **Experimental**
- **Type:** `string`

Restrict files that could be served via `/@fs/`. Accessing files outside this directory will result in a 403.
Restrict files that could be served via `/@fs/`. When `server.fsServe.strict` is set to `true`, accessing files outside this directory will result in a 403.

Vite will search for the root of the potential workspace and use it as default. A valid workspace met the following conditions, otherwise will fallback to the [project root](/guide/#index-html-and-project-root).
- contains `workspaces` field in `package.json`
- contains one of the following file
- `pnpm-workspace.yaml`

Accepts a path to specify the custom workspace root. Could be a absolute path or a path relative to [project root](/guide/#index-html-and-project-root). For example

```js
export default {
server: {
fsServe: {
// Allow serving files from one level up to the project root
root: '..'
}
}
}
```

## Build Options

### build.target
Expand Down
9 changes: 9 additions & 0 deletions packages/vite/src/node/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type LogLevel = LogType | 'silent'
export interface Logger {
info(msg: string, options?: LogOptions): void
warn(msg: string, options?: LogOptions): void
warnOnce(msg: string, options?: LogOptions): void
error(msg: string, options?: LogOptions): void
clearScreen(type: LogType): void
hasWarned: boolean
Expand Down Expand Up @@ -87,6 +88,8 @@ export function createLogger(
}
}

const warnedMessages = new Set<string>()

const logger: Logger = {
hasWarned: false,
info(msg, opts) {
Expand All @@ -96,6 +99,12 @@ export function createLogger(
logger.hasWarned = true
output('warn', msg, opts)
},
warnOnce(msg, opts) {
if (warnedMessages.has(msg)) return
logger.hasWarned = true
output('warn', msg, opts)
warnedMessages.add(msg)
},
error(msg, opts) {
logger.hasWarned = true
output('error', msg, opts)
Expand Down
20 changes: 18 additions & 2 deletions packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,22 @@ export interface ResolvedServerOptions extends ServerOptions {
}

export interface FileSystemServeOptions {
/**
* Strictly restrict file accessing outside of allowing paths.
*
* Default to false at this moment, will enabled by default in the future versions.
* @expiremental
* @default false
*/
strict?: boolean

/**
* Restrict accessing files outside this directory will result in a 403.
*
* Accepts absolute path or a path relative to project root.
* Will try to search up for workspace root by default.
*
* @expiremental
*/
root?: string
}
Expand Down Expand Up @@ -692,9 +703,14 @@ export function resolveServerOptions(
raw?: ServerOptions
): ResolvedServerOptions {
const server = raw || {}
const serverRoot = normalizePath(
const fsServeRoot = normalizePath(
path.resolve(root, server.fsServe?.root || searchForWorkspaceRoot(root))
)
server.fsServe = { root: serverRoot }
// TODO: make strict by default
const fsServeStrict = server.fsServe?.strict ?? false
server.fsServe = {
root: fsServeRoot,
strict: fsServeStrict
}
return server as ResolvedServerOptions
}
40 changes: 30 additions & 10 deletions packages/vite/src/node/server/middlewares/static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import os from 'os'
import path from 'path'
import sirv, { Options } from 'sirv'
import { Connect } from 'types/connect'
import { FileSystemServeOptions } from '..'
import { normalizePath, ResolvedConfig } from '../..'
import { FS_PREFIX } from '../../constants'
import { Logger } from '../../logger'
import { cleanUrl, fsPathFromId, isImportRequest } from '../../utils'
import { AccessRestrictedError } from './error'

Expand Down Expand Up @@ -92,7 +94,8 @@ export function serveRawFsMiddleware(
// restrict files outside of `fsServe.root`
ensureServingAccess(
path.resolve(fsPathFromId(url)),
config.server.fsServe.root
config.server.fsServe,
config.logger
)

url = url.slice(FS_PREFIX.length)
Expand All @@ -106,15 +109,32 @@ export function serveRawFsMiddleware(
}
}

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

const normalizedUrl = normalizePath(url)
if (!normalizedUrl.startsWith(serveRoot + path.posix.sep)) {
throw new AccessRestrictedError(
`The request url "${normalizedUrl}" is outside of vite dev server root "${serveRoot}".
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,
serveRoot
)
if (!normalizedUrl.startsWith(root + path.posix.sep)) {
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}"`)
}
}
}
2 changes: 1 addition & 1 deletion packages/vite/src/node/server/transformRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export async function transformRequest(
// as string
try {
if (!options.ssr) {
ensureServingAccess(file, config.server.fsServe.root)
ensureServingAccess(file, config.server.fsServe, config.logger)
}
code = await fs.readFile(file, 'utf-8')
isDebug && debugLoad(`${timeFrom(loadStart)} [fs] ${prettyUrl}`)
Expand Down

0 comments on commit 5433a65

Please sign in to comment.