Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vitejs/vite
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v4.5.5
Choose a base ref
...
head repository: vitejs/vite
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 9e460f58f91f95972ff9683f664fa4df44b6d2af
Choose a head ref
  • 7 commits
  • 24 files changed
  • 4 contributors

Commits on Sep 27, 2024

  1. docs: use search-only key

    yyx990803 authored and sapphi-red committed Sep 27, 2024
    1
    Copy the full SHA
    1dda64f View commit details

Commits on Nov 29, 2024

  1. docs: v4 show old document warning (#18805) (#18829)

    Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com>
    sapphi-red and bluwy authored Nov 29, 2024
    Copy the full SHA
    bb576ea View commit details

Commits on Jan 20, 2025

  1. Copy the full SHA
    07b36d5 View commit details
  2. Copy the full SHA
    c065a77 View commit details
  3. fix!: check host header to prevent DNS rebinding attacks and introduc…

    …e `server.allowedHosts`
    sapphi-red committed Jan 20, 2025
    Copy the full SHA
    ef1049d View commit details
  4. test: skip ws connection test for now

    it is covered by other tests
    sapphi-red committed Jan 20, 2025
    Copy the full SHA
    00f1aa2 View commit details
  5. release: v4.5.6

    sapphi-red committed Jan 20, 2025
    Copy the full SHA
    9e460f5 View commit details
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodejs 20.18.1
2 changes: 1 addition & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
@@ -112,7 +112,7 @@ export default defineConfig({

algolia: {
appId: '7H67QR5P0A',
apiKey: 'deaab78bcdfe96b599497d25acc6460e',
apiKey: '208bb9c14574939326032b937431014b',
indexName: 'vitejs',
searchParameters: {
facetFilters: ['tags:en'],
47 changes: 47 additions & 0 deletions docs/.vitepress/theme/components/OldDocument.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<div class="old-document">
<p>
This documentation covers Vite 4 <strong>(old version)</strong>. For the
latest version, see
<a href="https://vite.dev" class="new-document-link">https://vite.dev</a>.
</p>
</div>
</template>

<style>
:root {
--vp-layout-top-height: 96px;
@media (min-width: 455px) {
--vp-layout-top-height: 64px;
}
@media (min-width: 960px) {
--vp-layout-top-height: 32px;
}
}
.old-document {
position: fixed;
display: flex;
height: var(--vp-layout-top-height);
width: 100%;
padding: 4px 32px;
justify-content: center;
align-items: center;
color: var(--vp-c-text-1);
background: var(--vp-c-brand-lightest);
z-index: var(--vp-z-index-layout-top);
.new-document-link {
text-decoration: underline;
color: var(--vp-c-text-1);
&:hover {
color: var(--vp-c-text-2);
}
}
}
.dark {
.old-document {
background: var(--vp-c-brand-darker);
}
}
</style>
2 changes: 2 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -4,13 +4,15 @@ import './styles/vars.css'
import HomeSponsors from './components/HomeSponsors.vue'
import AsideSponsors from './components/AsideSponsors.vue'
import SvgImage from './components/SvgImage.vue'
import OldDocument from './components/OldDocument.vue'

export default {
...Theme,
Layout() {
return h(Theme.Layout, null, {
'home-features-after': () => h(HomeSponsors),
'aside-ads-before': () => h(AsideSponsors),
'layout-top': () => h(OldDocument),
})
},
enhanceApp({ app }) {
9 changes: 9 additions & 0 deletions docs/config/preview-options.md
Original file line number Diff line number Diff line change
@@ -17,6 +17,15 @@ See [`server.host`](./server-options#server-host) for more details.

:::

## preview.allowedHosts

- **Type:** `string | true`
- **Default:** [`server.allowedHosts`](./server-options#server-allowedhosts)

The hostnames that Vite is allowed to respond to.

See [`server.allowedHosts`](./server-options#server-allowedhosts) for more details.

## preview.port

- **Type:** `number`
23 changes: 22 additions & 1 deletion docs/config/server-options.md
Original file line number Diff line number Diff line change
@@ -41,6 +41,20 @@ See [the WSL document](https://learn.microsoft.com/en-us/windows/wsl/networking#

:::

## server.allowedHosts

- **Type:** `string[] | true`
- **Default:** `[]`

The hostnames that Vite is allowed to respond to.
`localhost` and domains under `.localhost` and all IP addresses are allowed by default.
When using HTTPS, this check is skipped.

If a string starts with `.`, it will allow that hostname without the `.` and all subdomains under the hostname. For example, `.example.com` will allow `example.com`, `foo.example.com`, and `foo.bar.example.com`.

If set to `true`, the server is allowed to respond to requests for any hosts.
This is not recommended as it will be vulnerable to DNS rebinding attacks.

## server.port

- **Type:** `number`
@@ -135,8 +149,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

6 changes: 6 additions & 0 deletions docs/guide/backend-integration.md
Original file line number Diff line number Diff line change
@@ -11,6 +11,12 @@ If you need a custom integration, you can follow the steps in this guide to conf
```js
// vite.config.js
export default defineConfig({
server: {
cors: {
// the origin you will be accessing via browser
origin: 'http://my-backend.example.com',
},
},
build: {
// generate manifest.json in outDir
manifest: true,
8 changes: 8 additions & 0 deletions packages/vite/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## <small>4.5.6 (2025-01-20)</small>

* fix!: check host header to prevent DNS rebinding attacks and introduce `server.allowedHosts` ([ef1049d](https://github.com/vitejs/vite/commit/ef1049d))
* fix!: default `server.cors: false` to disallow fetching from untrusted origins ([07b36d5](https://github.com/vitejs/vite/commit/07b36d5))
* fix: verify token for HMR WebSocket connection ([c065a77](https://github.com/vitejs/vite/commit/c065a77))



## <small>4.5.5 (2024-09-16)</small>


2 changes: 1 addition & 1 deletion packages/vite/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vite",
"version": "4.5.5",
"version": "4.5.6",
"type": "module",
"license": "MIT",
"author": "Evan You",
7 changes: 6 additions & 1 deletion packages/vite/src/client/client.ts
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ declare const __HMR_DIRECT_TARGET__: string
declare const __HMR_BASE__: string
declare const __HMR_TIMEOUT__: number
declare const __HMR_ENABLE_OVERLAY__: boolean
declare const __WS_TOKEN__: string

console.debug('[vite] connecting...')

@@ -29,6 +30,7 @@ const socketHost = `${__HMR_HOSTNAME__ || importMetaUrl.hostname}:${
}${__HMR_BASE__}`
const directSocketHost = __HMR_DIRECT_TARGET__
const base = __BASE__ || '/'
const wsToken = __WS_TOKEN__
const messageBuffer: string[] = []

let socket: WebSocket
@@ -74,7 +76,10 @@ function setupWebSocket(
hostAndPath: string,
onCloseWithoutOpen?: () => void,
) {
const socket = new WebSocket(`${protocol}://${hostAndPath}`, 'vite-hmr')
const socket = new WebSocket(
`${protocol}://${hostAndPath}?token=${wsToken}`,
'vite-hmr',
)
let isOpened = false

socket.addEventListener(
4 changes: 2 additions & 2 deletions packages/vite/src/node/__tests__/config.spec.ts
Original file line number Diff line number Diff line change
@@ -237,7 +237,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 () => {
@@ -274,7 +274,7 @@ describe('preview config', () => {
host: false,
https: false,
proxy: { '/bar': 'http://localhost:3010' },
cors: true,
cors: false,
})

test('preview overrides server config', async () => {
38 changes: 37 additions & 1 deletion packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import { pathToFileURL } from 'node:url'
import { promisify } from 'node:util'
import { performance } from 'node:perf_hooks'
import { createRequire } from 'node:module'
import crypto from 'node:crypto'
import colors from 'picocolors'
import type { Alias, AliasOptions } from 'dep-types/alias'
import aliasPlugin from '@rollup/plugin-alias'
@@ -70,6 +71,7 @@ import { findNearestPackageData } from './packages'
import { loadEnv, resolveEnvPrefix } from './env'
import type { ResolvedSSROptions, SSROptions } from './ssr'
import { resolveSSROptions } from './ssr'
import { getAdditionalAllowedHosts } from './server/middlewares/hostCheck'

const debug = createDebugger('vite:config')
const promisifiedRealpath = promisify(fs.realpath)
@@ -330,6 +332,18 @@ export interface LegacyOptions {
* @default false
*/
buildSsrCjsExternalHeuristics?: boolean
/**
* In Vite 6.0.8 / 5.4.11 / 4.5.5 and below, WebSocket server was able to connect from any web pages. However,
* that could be exploited by a malicious web page.
*
* In Vite 6.0.9+ / 5.4.12+ / 4.5.6+ the WebSocket server now requires a token to connect from a web page.
* But this may break some plugins and frameworks that connects to the WebSocket server
* on their own. Enabling this option will make Vite skip the token check.
*
* **We do not recommend enabling this option unless you are sure that you are fine with
* that security weakness.**
*/
skipWebSocketTokenCheck?: boolean
}

export interface ResolveWorkerOptions extends PluginHookUtils {
@@ -385,6 +399,19 @@ export type ResolvedConfig = Readonly<
worker: ResolveWorkerOptions
appType: AppType
experimental: ExperimentalOptions
/**
* The token to connect to the WebSocket server from browsers.
*
* We recommend using `import.meta.hot` rather than connecting
* to the WebSocket server directly.
* If you have a usecase that requires connecting to the WebSocket
* server, please create an issue so that we can discuss.
*
* @deprecated use `import.meta.hot`
*/
webSocketToken: string
/** @internal */
additionalAllowedHosts: string[]
} & PluginHookUtils
>

@@ -649,6 +676,8 @@ export async function resolveConfig(
config.legacy?.buildSsrCjsExternalHeuristics,
)

const preview = resolvePreviewOptions(config.preview, server)

const middlewareMode = config?.server?.middlewareMode

const optimizeDeps = config.optimizeDeps || {}
@@ -704,7 +733,7 @@ export async function resolveConfig(
},
server,
build: resolvedBuildOptions,
preview: resolvePreviewOptions(config.preview, server),
preview,
envDir,
env: {
...userEnv,
@@ -734,6 +763,13 @@ export async function resolveConfig(
hmrPartialAccept: false,
...config.experimental,
},
// random 72 bits (12 base64 chars)
// at least 64bits is recommended
// https://owasp.org/www-community/vulnerabilities/Insufficient_Session-ID_Length
webSocketToken: Buffer.from(
crypto.getRandomValues(new Uint8Array(9)),
).toString('base64url'),
additionalAllowedHosts: getAdditionalAllowedHosts(server, preview),
getSortedPlugins: undefined!,
getSortedPluginHooks: undefined!,
}
24 changes: 24 additions & 0 deletions packages/vite/src/node/http.ts
Original file line number Diff line number Diff line change
@@ -27,6 +27,18 @@ export interface CommonServerOptions {
* Set to 0.0.0.0 to listen on all addresses, including LAN and public addresses.
*/
host?: string | boolean
/**
* The hostnames that Vite is allowed to respond to.
* `localhost` and subdomains under `.localhost` and all IP addresses are allowed by default.
* When using HTTPS, this check is skipped.
*
* If a string starts with `.`, it will allow that hostname without the `.` and all subdomains under the hostname.
* For example, `.example.com` will allow `example.com`, `foo.example.com`, and `foo.bar.example.com`.
*
* If set to `true`, the server is allowed to respond to requests for any hosts.
* This is not recommended as it will be vulnerable to DNS rebinding attacks.
*/
allowedHosts?: string[] | true
/**
* Enable TLS + HTTP/2.
* Note: this downgrades to TLS only when the proxy option is also used.
@@ -62,8 +74,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
/**
@@ -76,6 +94,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
| ((origin: string, cb: (err: Error, origins: CorsOrigin) => void) => void)
2 changes: 2 additions & 0 deletions packages/vite/src/node/plugins/clientInjections.ts
Original file line number Diff line number Diff line change
@@ -65,6 +65,7 @@ export function clientInjectionsPlugin(config: ResolvedConfig): Plugin {
const hmrBaseReplacement = escapeReplacement(hmrBase)
const hmrTimeoutReplacement = escapeReplacement(timeout)
const hmrEnableOverlayReplacement = escapeReplacement(overlay)
const wsTokenReplacement = escapeReplacement(config.webSocketToken)

injectConfigValues = (code: string) => {
return code
@@ -79,6 +80,7 @@ export function clientInjectionsPlugin(config: ResolvedConfig): Plugin {
.replace(`__HMR_BASE__`, hmrBaseReplacement)
.replace(`__HMR_TIMEOUT__`, hmrTimeoutReplacement)
.replace(`__HMR_ENABLE_OVERLAY__`, hmrEnableOverlayReplacement)
.replace(`__WS_TOKEN__`, wsTokenReplacement)
}
},
transform(code, id, options) {
15 changes: 12 additions & 3 deletions packages/vite/src/node/preview.ts
Original file line number Diff line number Diff line change
@@ -19,8 +19,9 @@ import { proxyMiddleware } from './server/middlewares/proxy'
import { resolveHostname, resolveServerUrls, shouldServeFile } from './utils'
import { printServerUrls } from './logger'
import { DEFAULT_PREVIEW_PORT } from './constants'
import { resolveConfig } from '.'
import type { InlineConfig, ResolvedConfig } from '.'
import { resolveConfig } from './config'
import type { InlineConfig, ResolvedConfig } from './config'
import { hostCheckMiddleware } from './server/middlewares/hostCheck'

export interface PreviewOptions extends CommonServerOptions {}

@@ -37,6 +38,7 @@ export function resolvePreviewOptions(
port: preview?.port,
strictPort: preview?.strictPort ?? server.strictPort,
host: preview?.host ?? server.host,
allowedHosts: preview?.allowedHosts ?? server.allowedHosts,
https: preview?.https ?? server.https,
open: preview?.open ?? server.open,
proxy: preview?.proxy ?? server.proxy,
@@ -144,10 +146,17 @@ export async function preview(

// cors
const { cors } = config.preview
if (cors !== false) {
if (cors !== undefined && cors !== false) {
app.use(corsMiddleware(typeof cors === 'boolean' ? {} : cors))
}

// host check (to prevent DNS rebinding attacks)
const { allowedHosts } = config.preview
// no need to check for HTTPS as HTTPS is not vulnerable to DNS rebinding attacks
if (allowedHosts !== true && !config.preview.https) {
app.use(hostCheckMiddleware(config))
}

// proxy
const { proxy } = config.preview
if (proxy) {
Loading