From 7d1699ccf673e2790704756d89d2e1e4ee478fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Tue, 21 Jan 2025 19:20:07 +0900 Subject: [PATCH] fix: allow CORS from loopback addresses by default (#19249) --- docs/config/server-options.md | 2 +- .../vite/src/node/__tests__/constants.spec.ts | 32 +++++++++++++++++++ packages/vite/src/node/constants.ts | 7 ++++ packages/vite/src/node/preview.ts | 12 +++++-- packages/vite/src/node/server/index.ts | 16 ++++++++-- 5 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 packages/vite/src/node/__tests__/constants.spec.ts diff --git a/docs/config/server-options.md b/docs/config/server-options.md index 3a03fdf2971c72..f3c82a4a397eb6 100644 --- a/docs/config/server-options.md +++ b/docs/config/server-options.md @@ -151,7 +151,7 @@ export default defineConfig({ ## server.cors - **Type:** `boolean | CorsOptions` -- **Default:** `false` +- **Default:** `{ origin: /^https?:\/\/(?:(?:[^:]+\.)?localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/ }` (allows localhost, `127.0.0.1` and `::1`) 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. diff --git a/packages/vite/src/node/__tests__/constants.spec.ts b/packages/vite/src/node/__tests__/constants.spec.ts new file mode 100644 index 00000000000000..c7015f60104280 --- /dev/null +++ b/packages/vite/src/node/__tests__/constants.spec.ts @@ -0,0 +1,32 @@ +import { expect, test } from 'vitest' +import { defaultAllowedOrigins } from '../constants' + +test('defaultAllowedOrigins', () => { + const allowed = [ + 'http://localhost', + 'http://foo.localhost', + 'http://localhost:3000', + 'https://localhost:3000', + 'http://127.0.0.1', + 'http://[::1]', + 'http://[::1]:3000', + ] + const denied = [ + 'file:///foo', + 'http://localhost.example.com', + 'http://foo.example.com:localhost', + 'http://', + 'http://192.0.2', + 'http://[2001:db8::1]', + 'http://vite', + 'http://vite:3000', + ] + + for (const origin of allowed) { + expect(defaultAllowedOrigins.test(origin), origin).toBe(true) + } + + for (const origin of denied) { + expect(defaultAllowedOrigins.test(origin), origin).toBe(false) + } +}) diff --git a/packages/vite/src/node/constants.ts b/packages/vite/src/node/constants.ts index 1532c4612930eb..48cc26a9fe0c64 100644 --- a/packages/vite/src/node/constants.ts +++ b/packages/vite/src/node/constants.ts @@ -141,4 +141,11 @@ export const DEFAULT_PREVIEW_PORT = 4173 export const DEFAULT_ASSETS_INLINE_LIMIT = 4096 +// the regex to allow loopback address origins: +// - localhost domains (which will always resolve to the loopback address by RFC 6761 section 6.3) +// - 127.0.0.1 +// - ::1 +export const defaultAllowedOrigins = + /^https?:\/\/(?:(?:[^:]+\.)?localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/ + export const METADATA_FILENAME = '_metadata.json' diff --git a/packages/vite/src/node/preview.ts b/packages/vite/src/node/preview.ts index 3947fb1aa2ac0a..a431bb6a168379 100644 --- a/packages/vite/src/node/preview.ts +++ b/packages/vite/src/node/preview.ts @@ -5,7 +5,7 @@ import compression from '@polka/compression' import connect from 'connect' import type { Connect } from 'dep-types/connect' import corsMiddleware from 'cors' -import { DEFAULT_PREVIEW_PORT } from './constants' +import { DEFAULT_PREVIEW_PORT, defaultAllowedOrigins } from './constants' import type { HttpServer, ResolvedServerOptions, @@ -186,8 +186,14 @@ export async function preview( // cors const { cors } = config.preview - if (cors !== undefined && cors !== false) { - app.use(corsMiddleware(typeof cors === 'boolean' ? {} : cors)) + if (cors !== false) { + app.use( + corsMiddleware( + typeof cors === 'boolean' + ? {} + : (cors ?? { origin: defaultAllowedOrigins }), + ), + ) } // host check (to prevent DNS rebinding attacks) diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 9378a479fbb293..5372dc3d8576c0 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -46,7 +46,11 @@ import { ERR_OUTDATED_OPTIMIZED_DEP } from '../plugins/optimizedDeps' import { getDepsOptimizer, initDepsOptimizer } from '../optimizer' import { bindCLIShortcuts } from '../shortcuts' import type { BindCLIShortcutsOptions } from '../shortcuts' -import { CLIENT_DIR, DEFAULT_DEV_PORT } from '../constants' +import { + CLIENT_DIR, + DEFAULT_DEV_PORT, + defaultAllowedOrigins, +} from '../constants' import type { Logger } from '../logger' import { printServerUrls } from '../logger' import { @@ -850,8 +854,14 @@ export async function _createServer( // cors const { cors } = serverConfig - if (cors !== undefined && cors !== false) { - middlewares.use(corsMiddleware(typeof cors === 'boolean' ? {} : cors)) + if (cors !== false) { + middlewares.use( + corsMiddleware( + typeof cors === 'boolean' + ? {} + : (cors ?? { origin: defaultAllowedOrigins }), + ), + ) } // host check (to prevent DNS rebinding attacks)