Skip to content

Commit

Permalink
feat: server.fs.deny support
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Oct 24, 2021
1 parent 1f4723b commit fa86b17
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 2 deletions.
9 changes: 9 additions & 0 deletions docs/config/index.md
Expand Up @@ -593,6 +593,15 @@ createServer()
})
```

### server.fs.deny

- **Experimental**
- **Type:** `string[]`

Blocklist for sensitive files being restricted to be served by Vite dev server.

Default to `['.env', '.env.*', '*.{pem,crt}']`.

### server.origin

- **Type:** `string`
Expand Down
9 changes: 9 additions & 0 deletions packages/playground/fs-serve/__tests__/fs-serve.spec.ts
Expand Up @@ -41,6 +41,15 @@ describe('main', () => {
test('nested entry', async () => {
expect(await page.textContent('.nested-entry')).toBe('foobar')
})

test('nested entry', async () => {
expect(await page.textContent('.nested-entry')).toBe('foobar')
})

test('denied', async () => {
expect(await page.textContent('.unsafe-dotenv')).toBe('403')
expect(await page.textContent('.safe-root-file')).toBe('200')
})
} else {
test('dummy test to make jest happy', async () => {
// Your test suite must contain at least one test.
Expand Down
22 changes: 22 additions & 0 deletions packages/playground/fs-serve/root/src/index.html
Expand Up @@ -23,6 +23,10 @@ <h2>Unsafe /@fs/ Fetch</h2>
<h2>Nested Entry</h2>
<pre class="nested-entry"></pre>

<h2>Denied</h2>
<pre class="unsafe-dotenv"></pre>
<pre class="safe-root-file"></pre>

<script type="module">
import '../../entry'
import json, { msg } from '../../safe.json'
Expand Down Expand Up @@ -76,6 +80,24 @@ <h2>Nested Entry</h2>
console.error(e)
})

// .env, denied by default
fetch('/@fs/' + ROOT + '/root/.env')
.then((r) => {
text('.unsafe-dotenv', r.status)
})
.catch((e) => {
console.error(e)
})

// other files under the root, ok
fetch('/@fs/' + ROOT + '/root/vite.config.js')
.then((r) => {
text('.safe-root-file', r.status)
})
.catch((e) => {
console.error(e)
})

function text(sel, text) {
document.querySelector(sel).textContent = text
}
Expand Down
16 changes: 15 additions & 1 deletion packages/vite/src/node/server/index.ts
Expand Up @@ -162,6 +162,18 @@ export interface FileSystemServeOptions {
* @experimental
*/
allow?: string[]

/**
* Restrict accessing files that matches the patterns.
*
* This will have higher priority than `allow`.
* Glob patterns are supported.
*
* @default ['.env', '.env.*', '*.crt', '*.pem']
*
* @experimental
*/
deny?: string[]
}

/**
Expand Down Expand Up @@ -690,6 +702,7 @@ export function resolveServerOptions(
): ResolvedServerOptions {
const server = raw || {}
let allowDirs = server.fs?.allow
const deny = server.fs?.deny || ['.env', '.env.*', '*.{crt,pem}']

if (!allowDirs) {
allowDirs = [searchForWorkspaceRoot(root)]
Expand All @@ -706,7 +719,8 @@ export function resolveServerOptions(
server.fs = {
// TODO: make strict by default
strict: server.fs?.strict,
allow: allowDirs
allow: allowDirs,
deny
}
return server as ResolvedServerOptions
}
6 changes: 6 additions & 0 deletions packages/vite/src/node/server/middlewares/static.ts
Expand Up @@ -12,6 +12,7 @@ import {
isWindows,
slash
} from '../../utils'
import match from 'minimatch'
import { AccessRestrictedError } from './error'

const sirvOptions: Options = {
Expand Down Expand Up @@ -122,6 +123,8 @@ export function serveRawFsMiddleware(
}
}

const _matchOptions = { matchBase: true }

export function isFileServingAllowed(
url: string,
server: ViteDevServer
Expand All @@ -131,6 +134,9 @@ export function isFileServingAllowed(

const file = ensureLeadingSlash(normalizePath(cleanUrl(url)))

if (server.config.server.fs.deny.some((i) => match(file, i, _matchOptions)))
return false

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

if (server.config.server.fs.allow.some((i) => file.startsWith(i + '/')))
Expand Down
6 changes: 5 additions & 1 deletion packages/vite/types/shims.d.ts
Expand Up @@ -96,7 +96,11 @@ declare module 'rollup-plugin-web-worker-loader' {
}

declare module 'minimatch' {
function match(path: string, pattern: string): boolean
function match(
path: string,
pattern: string,
options?: { matchBase?: boolean }
): boolean
export default match
}

Expand Down

0 comments on commit fa86b17

Please sign in to comment.