Skip to content

Commit

Permalink
fix!: default server.cors: false to disallow fetching from untruste…
Browse files Browse the repository at this point in the history
…d origins
  • Loading branch information
sapphi-red committed Jan 20, 2025
1 parent c0f72a6 commit b09572a
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 9 deletions.
9 changes: 8 additions & 1 deletion docs/config/server-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,15 @@ export default defineConfig({
## server.cors

- **Type:** `boolean | CorsOptions`
- **Default:** `false`

Configure CORS for the dev server. Pass an [options object](https://github.com/expressjs/cors#configuration-options) to fine tune the behavior or `true` to allow any origin.

:::warning

Configure CORS for the dev server. This is enabled by default and allows any origin. Pass an [options object](https://github.com/expressjs/cors#configuration-options) to fine tune the behavior or `false` to disable.
We recommend setting a specific value rather than `true` to avoid exposing the source code to untrusted origins.

:::

## server.headers

Expand Down
6 changes: 6 additions & 0 deletions docs/guide/backend-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ If you need a custom integration, you can follow the steps in this guide to conf
import { defineConfig } from 'vite'
// ---cut---
export default defineConfig({
server: {
cors: {
// the origin you will be accessing via browser
origin: 'http://my-backend.example.com',
},
},
build: {
// generate .vite/manifest.json in outDir
manifest: true,
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/node/__tests__/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ describe('preview config', () => {
'Cache-Control': 'no-store',
},
proxy: { '/foo': 'http://localhost:4567' },
cors: false,
cors: true,
})

test('preview inherits server config with default port', async () => {
Expand Down Expand Up @@ -285,7 +285,7 @@ describe('preview config', () => {
open: false,
host: false,
proxy: { '/bar': 'http://localhost:3010' },
cors: true,
cors: false,
})

test('preview overrides server config', async () => {
Expand Down
12 changes: 12 additions & 0 deletions packages/vite/src/node/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,14 @@ export interface CommonServerOptions {
/**
* Configure CORS for the dev server.
* Uses https://github.com/expressjs/cors.
*
* When enabling this option, **we recommend setting a specific value
* rather than `true`** to avoid exposing the source code to untrusted origins.
*
* Set to `true` to allow all methods from any origin, or configure separately
* using an object.
*
* @default false
*/
cors?: CorsOptions | boolean
/**
Expand All @@ -73,6 +79,12 @@ export interface CommonServerOptions {
* https://github.com/expressjs/cors#configuration-options
*/
export interface CorsOptions {
/**
* Configures the Access-Control-Allow-Origin CORS header.
*
* **We recommend setting a specific value rather than
* `true`** to avoid exposing the source code to untrusted origins.
*/
origin?:
| CorsOrigin
| ((
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@ export async function _createServer(
middlewares.use(timeMiddleware(root))
}

// cors (enabled by default)
// cors
const { cors } = serverConfig
if (cors !== false) {
middlewares.use(corsMiddleware(typeof cors === 'boolean' ? {} : cors))
Expand Down Expand Up @@ -1046,7 +1046,7 @@ export const serverConfigDefaults = Object.freeze({
https: undefined,
open: false,
proxy: undefined,
cors: true,
cors: false,
headers: {},
// hmr
// ws
Expand Down
77 changes: 73 additions & 4 deletions playground/fs-serve/__tests__/fs-serve.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import fetch from 'node-fetch'
import { beforeAll, describe, expect, test } from 'vitest'
import {
afterEach,
beforeAll,
beforeEach,
describe,
expect,
test,
} from 'vitest'
import type { Page } from 'playwright-chromium'
import testJSON from '../safe.json'
import { isServe, page, viteTestUrl } from '~utils'
import { browser, isServe, page, viteTestUrl } from '~utils'

const getViteTestIndexHtmlUrl = () => {
const srcPrefix = viteTestUrl.endsWith('/') ? '' : '/'
// NOTE: viteTestUrl is set lazily
return viteTestUrl + srcPrefix + 'src/'
}

const stringified = JSON.stringify(testJSON)

describe.runIf(isServe)('main', () => {
beforeAll(async () => {
const srcPrefix = viteTestUrl.endsWith('/') ? '' : '/'
await page.goto(viteTestUrl + srcPrefix + 'src/')
await page.goto(getViteTestIndexHtmlUrl())
})

test('default import', async () => {
Expand Down Expand Up @@ -113,3 +126,59 @@ describe('fetch', () => {
expect(res.headers.get('x-served-by')).toBe('vite')
})
})

describe('cross origin', () => {
const fetchStatusFromPage = async (page: Page, url: string) => {
return await page.evaluate(async (url: string) => {
try {
const res = await globalThis.fetch(url)
return res.status
} catch {
return -1
}
}, url)
}

describe('allowed for same origin', () => {
beforeEach(async () => {
await page.goto(getViteTestIndexHtmlUrl())
})

test('fetch HTML file', async () => {
const status = await fetchStatusFromPage(page, viteTestUrl + '/src/')
expect(status).toBe(200)
})

test.runIf(isServe)('fetch JS file', async () => {
const status = await fetchStatusFromPage(
page,
viteTestUrl + '/src/code.js',
)
expect(status).toBe(200)
})
})

describe('denied for different origin', async () => {
let page2: Page
beforeEach(async () => {
page2 = await browser.newPage()
await page2.goto('http://vite.dev/404')
})
afterEach(async () => {
await page2.close()
})

test('fetch HTML file', async () => {
const status = await fetchStatusFromPage(page2, viteTestUrl + '/src/')
expect(status).not.toBe(200)
})

test.runIf(isServe)('fetch JS file', async () => {
const status = await fetchStatusFromPage(
page2,
viteTestUrl + '/src/code.js',
)
expect(status).not.toBe(200)
})
})
})
1 change: 1 addition & 0 deletions playground/fs-serve/root/src/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// code.js
1 change: 1 addition & 0 deletions playground/fs-serve/root/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ <h2>Denied</h2>
<script type="module">
import '../../entry'
import json, { msg } from '../../safe.json'
import './code.js'

function joinUrlSegments(a, b) {
if (!a || !b) {
Expand Down

0 comments on commit b09572a

Please sign in to comment.