Skip to content

Commit dfea38f

Browse files
committed
fix!: default server.cors: false to disallow fetching from untrusted origins
1 parent 5da6895 commit dfea38f

File tree

9 files changed

+106
-10
lines changed

9 files changed

+106
-10
lines changed

docs/config/server-options.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,15 @@ export default defineConfig({
137137
## server.cors
138138

139139
- **Type:** `boolean | CorsOptions`
140+
- **Default:** `false`
141+
142+
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.
143+
144+
:::warning
140145

141-
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.
146+
We recommend setting a specific value rather than `true` to avoid exposing the source code to untrusted origins.
147+
148+
:::
142149

143150
## server.headers
144151

docs/guide/backend-integration.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ If you need a custom integration, you can follow the steps in this guide to conf
1313
// ---cut---
1414
// vite.config.js
1515
export default defineConfig({
16+
server: {
17+
cors: {
18+
// the origin you will be accessing via browser
19+
origin: 'http://my-backend.example.com',
20+
},
21+
},
1622
build: {
1723
// generate .vite/manifest.json in outDir
1824
manifest: true,

packages/vite/src/node/__tests__/config.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ describe('preview config', () => {
249249
'Cache-Control': 'no-store',
250250
},
251251
proxy: { '/foo': 'http://localhost:4567' },
252-
cors: false,
252+
cors: true,
253253
})
254254

255255
test('preview inherits server config with default port', async () => {
@@ -285,7 +285,7 @@ describe('preview config', () => {
285285
open: false,
286286
host: false,
287287
proxy: { '/bar': 'http://localhost:3010' },
288-
cors: true,
288+
cors: false,
289289
})
290290

291291
test('preview overrides server config', async () => {

packages/vite/src/node/http.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,14 @@ export interface CommonServerOptions {
5959
/**
6060
* Configure CORS for the dev server.
6161
* Uses https://github.com/expressjs/cors.
62+
*
63+
* When enabling this option, **we recommend setting a specific value
64+
* rather than `true`** to avoid exposing the source code to untrusted origins.
65+
*
6266
* Set to `true` to allow all methods from any origin, or configure separately
6367
* using an object.
68+
*
69+
* @default false
6470
*/
6571
cors?: CorsOptions | boolean
6672
/**
@@ -73,6 +79,12 @@ export interface CommonServerOptions {
7379
* https://github.com/expressjs/cors#configuration-options
7480
*/
7581
export interface CorsOptions {
82+
/**
83+
* Configures the Access-Control-Allow-Origin CORS header.
84+
*
85+
* **We recommend setting a specific value rather than
86+
* `true`** to avoid exposing the source code to untrusted origins.
87+
*/
7688
origin?:
7789
| CorsOrigin
7890
| ((

packages/vite/src/node/preview.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export async function preview(
184184

185185
// cors
186186
const { cors } = config.preview
187-
if (cors !== false) {
187+
if (cors !== undefined && cors !== false) {
188188
app.use(corsMiddleware(typeof cors === 'boolean' ? {} : cors))
189189
}
190190

packages/vite/src/node/server/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -847,9 +847,9 @@ export async function _createServer(
847847
middlewares.use(timeMiddleware(root))
848848
}
849849

850-
// cors (enabled by default)
850+
// cors
851851
const { cors } = serverConfig
852-
if (cors !== false) {
852+
if (cors !== undefined && cors !== false) {
853853
middlewares.use(corsMiddleware(typeof cors === 'boolean' ? {} : cors))
854854
}
855855

playground/fs-serve/__tests__/fs-serve.spec.ts

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
import fetch from 'node-fetch'
2-
import { beforeAll, describe, expect, test } from 'vitest'
2+
import {
3+
afterEach,
4+
beforeAll,
5+
beforeEach,
6+
describe,
7+
expect,
8+
test,
9+
} from 'vitest'
10+
import type { Page } from 'playwright-chromium'
311
import testJSON from '../safe.json'
4-
import { isServe, page, viteTestUrl } from '~utils'
12+
import { browser, isServe, page, viteTestUrl } from '~utils'
13+
14+
const getViteTestIndexHtmlUrl = () => {
15+
const srcPrefix = viteTestUrl.endsWith('/') ? '' : '/'
16+
// NOTE: viteTestUrl is set lazily
17+
return viteTestUrl + srcPrefix + 'src/'
18+
}
519

620
const stringified = JSON.stringify(testJSON)
721

822
describe.runIf(isServe)('main', () => {
923
beforeAll(async () => {
10-
const srcPrefix = viteTestUrl.endsWith('/') ? '' : '/'
11-
await page.goto(viteTestUrl + srcPrefix + 'src/')
24+
await page.goto(getViteTestIndexHtmlUrl())
1225
})
1326

1427
test('default import', async () => {
@@ -113,3 +126,59 @@ describe('fetch', () => {
113126
expect(res.headers.get('x-served-by')).toBe('vite')
114127
})
115128
})
129+
130+
describe('cross origin', () => {
131+
const fetchStatusFromPage = async (page: Page, url: string) => {
132+
return await page.evaluate(async (url: string) => {
133+
try {
134+
const res = await globalThis.fetch(url)
135+
return res.status
136+
} catch {
137+
return -1
138+
}
139+
}, url)
140+
}
141+
142+
describe('allowed for same origin', () => {
143+
beforeEach(async () => {
144+
await page.goto(getViteTestIndexHtmlUrl())
145+
})
146+
147+
test('fetch HTML file', async () => {
148+
const status = await fetchStatusFromPage(page, viteTestUrl + '/src/')
149+
expect(status).toBe(200)
150+
})
151+
152+
test.runIf(isServe)('fetch JS file', async () => {
153+
const status = await fetchStatusFromPage(
154+
page,
155+
viteTestUrl + '/src/code.js',
156+
)
157+
expect(status).toBe(200)
158+
})
159+
})
160+
161+
describe('denied for different origin', async () => {
162+
let page2: Page
163+
beforeEach(async () => {
164+
page2 = await browser.newPage()
165+
await page2.goto('http://vite.dev/404')
166+
})
167+
afterEach(async () => {
168+
await page2.close()
169+
})
170+
171+
test('fetch HTML file', async () => {
172+
const status = await fetchStatusFromPage(page2, viteTestUrl + '/src/')
173+
expect(status).not.toBe(200)
174+
})
175+
176+
test.runIf(isServe)('fetch JS file', async () => {
177+
const status = await fetchStatusFromPage(
178+
page2,
179+
viteTestUrl + '/src/code.js',
180+
)
181+
expect(status).not.toBe(200)
182+
})
183+
})
184+
})

playground/fs-serve/root/src/code.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// code.js

playground/fs-serve/root/src/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ <h2>Denied</h2>
5252
<script type="module">
5353
import '../../entry'
5454
import json, { msg } from '../../safe.json'
55+
import './code.js'
5556

5657
function joinUrlSegments(a, b) {
5758
if (!a || !b) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy