Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: server.fs.deny support #5378

Merged
merged 5 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
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
8 changes: 8 additions & 0 deletions packages/playground/fs-serve/__tests__/fs-serve.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ 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')
antfu marked this conversation as resolved.
Show resolved Hide resolved
})
} else {
test('dummy test to make jest happy', async () => {
// Your test suite must contain at least one test.
Expand Down
2 changes: 1 addition & 1 deletion packages/playground/fs-serve/root/src/.env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
KEY=safe
KEY=unsafe
18 changes: 15 additions & 3 deletions packages/playground/fs-serve/root/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ <h2>Unsafe /@fs/ Fetch</h2>
<h2>Nested Entry</h2>
<pre class="nested-entry"></pre>

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

<script type="module">
import '../../entry'
import json, { msg } from '../../safe.json'
Expand All @@ -31,7 +34,7 @@ <h2>Nested Entry</h2>
text('.named', msg)

// inside allowed dir, safe fetch
fetch('/src/.env')
fetch('/src/safe.txt')
.then((r) => {
text('.safe-fetch-status', r.status)
return r.text()
Expand All @@ -41,7 +44,7 @@ <h2>Nested Entry</h2>
})

// outside of allowed dir, treated as unsafe
fetch('/.env')
fetch('/unsafe.txt')
.then((r) => {
text('.unsafe-fetch-status', r.status)
return r.text()
Expand Down Expand Up @@ -76,7 +79,16 @@ <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)
})

function text(sel, text) {
document.querySelector(sel).textContent = text
}
</script>
</script>
1 change: 1 addition & 0 deletions packages/playground/fs-serve/root/src/safe.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
KEY=safe
16 changes: 15 additions & 1 deletion packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
slash,
isFileReadable
} from '../../utils'
import match from 'minimatch'

const sirvOptions: Options = {
dev: true,
Expand Down Expand Up @@ -130,6 +131,8 @@ export function serveRawFsMiddleware(
}
}

const _matchOptions = { matchBase: true }

export function isFileServingAllowed(
url: string,
server: ViteDevServer
Expand All @@ -140,6 +143,9 @@ export function isFileServingAllowed(
const cleanedUrl = cleanUrl(url)
const file = ensureLeadingSlash(normalizePath(cleanedUrl))

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
Original file line number Diff line number Diff line change
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