CVE-2024-45811
Description
Vite a frontend build tooling framework for javascript. In affected versions the contents of arbitrary files can be returned to the browser. @fs denies access to files outside of Vite serving allow list. Adding ?import&raw to the URL bypasses this limitation and returns the file content if it exists. This issue has been patched in versions 5.4.6, 5.3.6, 5.2.14, 4.5.5, and 3.2.11. Users are advised to upgrade. There are no known workarounds for this vulnerability.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
vitenpm | >= 5.4.0, < 5.4.6 | 5.4.6 |
vitenpm | >= 5.3.0, < 5.3.6 | 5.3.6 |
vitenpm | >= 5.2.0, < 5.2.14 | 5.2.14 |
vitenpm | >= 4.0.0, < 4.5.4 | 4.5.4 |
vitenpm | < 3.2.11 | 3.2.11 |
vitenpm | >= 5.0.0, < 5.1.8 | 5.1.8 |
Patches
10f9691767ad76f469ceb5d883673ae1607ddef1d8845fff4845b8644543f1b901438f99e6fix: backport #18112, fs raw query
5 files changed · +36 −3
packages/vite/src/node/plugins/asset.ts+1 −1 modified@@ -29,7 +29,7 @@ import { FS_PREFIX } from '../constants' export const assetUrlRE = /__VITE_ASSET__([a-z\d]+)__(?:\$_(.*?)__)?/g -const rawRE = /(?:\?|&)raw(?:&|$)/ +export const rawRE = /(?:\?|&)raw(?:&|$)/ export const urlRE = /(\?|&)url(?:&|$)/ const jsSourceMapRE = /\.[cm]?js\.map$/ const unnededFinalQueryCharRE = /[?&]$/
packages/vite/src/node/server/middlewares/static.ts+1 −1 modified@@ -211,7 +211,7 @@ export function isFileServingAllowed( return false } -function ensureServingAccess( +export function ensureServingAccess( url: string, server: ViteDevServer, res: ServerResponse,
packages/vite/src/node/server/middlewares/transform.ts+9 −1 modified@@ -38,7 +38,8 @@ import { } from '../../plugins/optimizedDeps' import { ERR_CLOSED_SERVER } from '../pluginContainer' import { getDepsOptimizer } from '../../optimizer' -import { urlRE } from '../../plugins/asset' +import { rawRE, urlRE } from '../../plugins/asset' +import { ensureServingAccess } from './static' const debugCache = createDebugger('vite:cache') @@ -166,6 +167,13 @@ export function transformMiddleware( } } + if ( + (rawRE.test(url) || urlRE.test(url)) && + !ensureServingAccess(url, server, res, next) + ) { + return + } + if ( isJSRequest(url) || isImportRequest(url) ||
playground/fs-serve/root/src/index.html+20 −0 modified@@ -35,6 +35,8 @@ <h2>Safe /@fs/ Fetch</h2> <h2>Unsafe /@fs/ Fetch</h2> <pre class="unsafe-fs-fetch-status"></pre> <pre class="unsafe-fs-fetch"></pre> +<pre class="unsafe-fs-fetch-raw-status"></pre> +<pre class="unsafe-fs-fetch-raw"></pre> <pre class="unsafe-fs-fetch-8498-status"></pre> <pre class="unsafe-fs-fetch-8498"></pre> <pre class="unsafe-fs-fetch-8498-2-status"></pre> @@ -188,6 +190,24 @@ <h2>Denied</h2> console.error(e) }) + // not imported before, outside of root, treated as unsafe + fetch( + joinUrlSegments( + base, + joinUrlSegments('/@fs/', ROOT) + '/unsafe.json?import&raw', + ), + ) + .then((r) => { + text('.unsafe-fs-fetch-raw-status', r.status) + return r.json() + }) + .then((data) => { + text('.unsafe-fs-fetch-raw', JSON.stringify(data)) + }) + .catch((e) => { + console.error(e) + }) + // outside root with special characters #8498 fetch( joinUrlSegments(
playground/fs-serve/__tests__/fs-serve.spec.ts+5 −0 modified@@ -77,6 +77,11 @@ describe.runIf(isServe)('main', () => { expect(await page.textContent('.unsafe-fs-fetch-status')).toBe('403') }) + test('unsafe fs fetch', async () => { + expect(await page.textContent('.unsafe-fs-fetch-raw')).toBe('') + expect(await page.textContent('.unsafe-fs-fetch-raw-status')).toBe('403') + }) + test('unsafe fs fetch with special characters (#8498)', async () => { expect(await page.textContent('.unsafe-fs-fetch-8498')).toBe('') expect(await page.textContent('.unsafe-fs-fetch-8498-status')).toBe('404')
a6da45082b6efix: backport #18112, fs raw query
5 files changed · +37 −3
packages/vite/src/node/plugins/asset.ts+2 −2 modified@@ -29,8 +29,8 @@ export const duplicateAssets = new WeakMap< Map<string, OutputAsset> >() -const rawRE = /(\?|&)raw(?:&|$)/ -const urlRE = /(\?|&)url(?:&|$)/ +export const rawRE = /(\?|&)raw(?:&|$)/ +export const urlRE = /(\?|&)url(?:&|$)/ const assetCache = new WeakMap<ResolvedConfig, Map<string, string>>()
packages/vite/src/node/server/middlewares/static.ts+1 −1 modified@@ -174,7 +174,7 @@ export function isFileServingAllowed( return false } -function ensureServingAccess( +export function ensureServingAccess( url: string, server: ViteDevServer, res: ServerResponse,
packages/vite/src/node/server/middlewares/transform.ts+9 −0 modified@@ -35,6 +35,8 @@ import { ERR_OUTDATED_OPTIMIZED_DEP } from '../../plugins/optimizedDeps' import { getDepsOptimizer } from '../../optimizer' +import { rawRE, urlRE } from '../../plugins/asset' +import { ensureServingAccess } from './static' const debugCache = createDebugger('vite:cache') const isDebug = !!process.env.DEBUG @@ -147,6 +149,13 @@ export function transformMiddleware( } } + if ( + (rawRE.test(url) || urlRE.test(url)) && + !ensureServingAccess(url, server, res, next) + ) { + return + } + if ( isJSRequest(url) || isImportRequest(url) ||
playground/fs-serve/root/src/index.html+20 −0 modified@@ -35,6 +35,8 @@ <h2>Safe /@fs/ Fetch</h2> <h2>Unsafe /@fs/ Fetch</h2> <pre class="unsafe-fs-fetch-status"></pre> <pre class="unsafe-fs-fetch"></pre> +<pre class="unsafe-fs-fetch-raw-status"></pre> +<pre class="unsafe-fs-fetch-raw"></pre> <pre class="unsafe-fs-fetch-8498-status"></pre> <pre class="unsafe-fs-fetch-8498"></pre> <pre class="unsafe-fs-fetch-8498-2-status"></pre> @@ -166,6 +168,24 @@ <h2>Denied</h2> console.error(e) }) + // not imported before, outside of root, treated as unsafe + fetch( + joinUrlSegments( + base, + joinUrlSegments('/@fs/', ROOT) + '/unsafe.json?import&raw', + ), + ) + .then((r) => { + text('.unsafe-fs-fetch-raw-status', r.status) + return r.json() + }) + .then((data) => { + text('.unsafe-fs-fetch-raw', JSON.stringify(data)) + }) + .catch((e) => { + console.error(e) + }) + // outside root with special characters #8498 fetch('/@fs/' + ROOT + '/root/src/%2e%2e%2f%2e%2e%2funsafe%2ejson') .then((r) => {
playground/fs-serve/__tests__/fs-serve.spec.ts+5 −0 modified@@ -76,6 +76,11 @@ describe.runIf(isServe)('main', () => { expect(await page.textContent('.unsafe-fs-fetch-status')).toBe('403') }) + test('unsafe fs fetch', async () => { + expect(await page.textContent('.unsafe-fs-fetch-raw')).toBe('') + expect(await page.textContent('.unsafe-fs-fetch-raw-status')).toBe('403') + }) + test('unsafe fs fetch with special characters (#8498)', async () => { expect(await page.textContent('.unsafe-fs-fetch-8498')).toBe('') expect(await page.textContent('.unsafe-fs-fetch-8498-status')).toBe('403')
4 files changed · +35 −1
packages/vite/src/node/server/middlewares/static.ts+1 −1 modified@@ -229,7 +229,7 @@ export function isFileServingAllowed( return false } -function ensureServingAccess( +export function ensureServingAccess( url: string, server: ViteDevServer, res: ServerResponse,
packages/vite/src/node/server/middlewares/transform.ts+9 −0 modified@@ -12,6 +12,7 @@ import { isJSRequest, normalizePath, prettifyUrl, + rawRE, removeImportQuery, removeTimestampQuery, urlRE, @@ -35,6 +36,7 @@ import { ERR_CLOSED_SERVER } from '../pluginContainer' import { getDepsOptimizer } from '../../optimizer' import { cleanUrl, unwrapId, withTrailingSlash } from '../../../shared/utils' import { NULL_BYTE_PLACEHOLDER } from '../../../shared/constants' +import { ensureServingAccess } from './static' const debugCache = createDebugger('vite:cache') @@ -158,6 +160,13 @@ export function transformMiddleware( warnAboutExplicitPublicPathInUrl(url) } + if ( + (rawRE.test(url) || urlRE.test(url)) && + !ensureServingAccess(url, server, res, next) + ) { + return + } + if ( isJSRequest(url) || isImportRequest(url) ||
playground/fs-serve/root/src/index.html+20 −0 modified@@ -35,6 +35,8 @@ <h2>Safe /@fs/ Fetch</h2> <h2>Unsafe /@fs/ Fetch</h2> <pre class="unsafe-fs-fetch-status"></pre> <pre class="unsafe-fs-fetch"></pre> +<pre class="unsafe-fs-fetch-raw-status"></pre> +<pre class="unsafe-fs-fetch-raw"></pre> <pre class="unsafe-fs-fetch-8498-status"></pre> <pre class="unsafe-fs-fetch-8498"></pre> <pre class="unsafe-fs-fetch-8498-2-status"></pre> @@ -188,6 +190,24 @@ <h2>Denied</h2> console.error(e) }) + // not imported before, outside of root, treated as unsafe + fetch( + joinUrlSegments( + base, + joinUrlSegments('/@fs/', ROOT) + '/unsafe.json?import&raw', + ), + ) + .then((r) => { + text('.unsafe-fs-fetch-raw-status', r.status) + return r.json() + }) + .then((data) => { + text('.unsafe-fs-fetch-raw', JSON.stringify(data)) + }) + .catch((e) => { + console.error(e) + }) + // outside root with special characters #8498 fetch( joinUrlSegments(
playground/fs-serve/__tests__/fs-serve.spec.ts+5 −0 modified@@ -77,6 +77,11 @@ describe.runIf(isServe)('main', () => { expect(await page.textContent('.unsafe-fs-fetch-status')).toBe('403') }) + test('unsafe fs fetch', async () => { + expect(await page.textContent('.unsafe-fs-fetch-raw')).toBe('') + expect(await page.textContent('.unsafe-fs-fetch-raw-status')).toBe('403') + }) + test('unsafe fs fetch with special characters (#8498)', async () => { expect(await page.textContent('.unsafe-fs-fetch-8498')).toBe('') expect(await page.textContent('.unsafe-fs-fetch-8498-status')).toBe('404')
4 files changed · +35 −1
packages/vite/src/node/server/middlewares/static.ts+1 −1 modified@@ -232,7 +232,7 @@ export function isFileServingAllowed( return false } -function ensureServingAccess( +export function ensureServingAccess( url: string, server: ViteDevServer, res: ServerResponse,
packages/vite/src/node/server/middlewares/transform.ts+9 −0 modified@@ -12,6 +12,7 @@ import { isJSRequest, normalizePath, prettifyUrl, + rawRE, removeImportQuery, removeTimestampQuery, urlRE, @@ -35,6 +36,7 @@ import { ERR_CLOSED_SERVER } from '../pluginContainer' import { getDepsOptimizer } from '../../optimizer' import { cleanUrl, unwrapId, withTrailingSlash } from '../../../shared/utils' import { NULL_BYTE_PLACEHOLDER } from '../../../shared/constants' +import { ensureServingAccess } from './static' const debugCache = createDebugger('vite:cache') @@ -161,6 +163,13 @@ export function transformMiddleware( warnAboutExplicitPublicPathInUrl(url) } + if ( + (rawRE.test(url) || urlRE.test(url)) && + !ensureServingAccess(url, server, res, next) + ) { + return + } + if ( isJSRequest(url) || isImportRequest(url) ||
playground/fs-serve/root/src/index.html+20 −0 modified@@ -35,6 +35,8 @@ <h2>Safe /@fs/ Fetch</h2> <h2>Unsafe /@fs/ Fetch</h2> <pre class="unsafe-fs-fetch-status"></pre> <pre class="unsafe-fs-fetch"></pre> +<pre class="unsafe-fs-fetch-raw-status"></pre> +<pre class="unsafe-fs-fetch-raw"></pre> <pre class="unsafe-fs-fetch-8498-status"></pre> <pre class="unsafe-fs-fetch-8498"></pre> <pre class="unsafe-fs-fetch-8498-2-status"></pre> @@ -188,6 +190,24 @@ <h2>Denied</h2> console.error(e) }) + // not imported before, outside of root, treated as unsafe + fetch( + joinUrlSegments( + base, + joinUrlSegments('/@fs/', ROOT) + '/unsafe.json?import&raw', + ), + ) + .then((r) => { + text('.unsafe-fs-fetch-raw-status', r.status) + return r.json() + }) + .then((data) => { + text('.unsafe-fs-fetch-raw', JSON.stringify(data)) + }) + .catch((e) => { + console.error(e) + }) + // outside root with special characters #8498 fetch( joinUrlSegments(
playground/fs-serve/__tests__/fs-serve.spec.ts+5 −0 modified@@ -77,6 +77,11 @@ describe.runIf(isServe)('main', () => { expect(await page.textContent('.unsafe-fs-fetch-status')).toBe('403') }) + test('unsafe fs fetch', async () => { + expect(await page.textContent('.unsafe-fs-fetch-raw')).toBe('') + expect(await page.textContent('.unsafe-fs-fetch-raw-status')).toBe('403') + }) + test('unsafe fs fetch with special characters (#8498)', async () => { expect(await page.textContent('.unsafe-fs-fetch-8498')).toBe('') expect(await page.textContent('.unsafe-fs-fetch-8498-status')).toBe('404')
4 files changed · +35 −1
packages/vite/src/node/server/middlewares/static.ts+1 −1 modified@@ -229,7 +229,7 @@ export function isFileServingAllowed( return false } -function ensureServingAccess( +export function ensureServingAccess( url: string, server: ViteDevServer, res: ServerResponse,
packages/vite/src/node/server/middlewares/transform.ts+9 −0 modified@@ -12,6 +12,7 @@ import { isJSRequest, normalizePath, prettifyUrl, + rawRE, removeImportQuery, removeTimestampQuery, urlRE, @@ -35,6 +36,7 @@ import { ERR_CLOSED_SERVER } from '../pluginContainer' import { getDepsOptimizer } from '../../optimizer' import { cleanUrl, unwrapId, withTrailingSlash } from '../../../shared/utils' import { NULL_BYTE_PLACEHOLDER } from '../../../shared/constants' +import { ensureServingAccess } from './static' const debugCache = createDebugger('vite:cache') @@ -158,6 +160,13 @@ export function transformMiddleware( warnAboutExplicitPublicPathInUrl(url) } + if ( + (rawRE.test(url) || urlRE.test(url)) && + !ensureServingAccess(url, server, res, next) + ) { + return + } + if ( isJSRequest(url) || isImportRequest(url) ||
playground/fs-serve/root/src/index.html+20 −0 modified@@ -35,6 +35,8 @@ <h2>Safe /@fs/ Fetch</h2> <h2>Unsafe /@fs/ Fetch</h2> <pre class="unsafe-fs-fetch-status"></pre> <pre class="unsafe-fs-fetch"></pre> +<pre class="unsafe-fs-fetch-raw-status"></pre> +<pre class="unsafe-fs-fetch-raw"></pre> <pre class="unsafe-fs-fetch-8498-status"></pre> <pre class="unsafe-fs-fetch-8498"></pre> <pre class="unsafe-fs-fetch-8498-2-status"></pre> @@ -188,6 +190,24 @@ <h2>Denied</h2> console.error(e) }) + // not imported before, outside of root, treated as unsafe + fetch( + joinUrlSegments( + base, + joinUrlSegments('/@fs/', ROOT) + '/unsafe.json?import&raw', + ), + ) + .then((r) => { + text('.unsafe-fs-fetch-raw-status', r.status) + return r.json() + }) + .then((data) => { + text('.unsafe-fs-fetch-raw', JSON.stringify(data)) + }) + .catch((e) => { + console.error(e) + }) + // outside root with special characters #8498 fetch( joinUrlSegments(
playground/fs-serve/__tests__/fs-serve.spec.ts+5 −0 modified@@ -77,6 +77,11 @@ describe.runIf(isServe)('main', () => { expect(await page.textContent('.unsafe-fs-fetch-status')).toBe('403') }) + test('unsafe fs fetch', async () => { + expect(await page.textContent('.unsafe-fs-fetch-raw')).toBe('') + expect(await page.textContent('.unsafe-fs-fetch-raw-status')).toBe('403') + }) + test('unsafe fs fetch with special characters (#8498)', async () => { expect(await page.textContent('.unsafe-fs-fetch-8498')).toBe('') expect(await page.textContent('.unsafe-fs-fetch-8498-status')).toBe('404')
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-9cwx-2883-4wfxghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-45811ghsaADVISORY
- github.com/vitejs/vite/commit/4573a6fd6f1b097fb7296a3e135e0646b996b249ghsaWEB
- github.com/vitejs/vite/commit/6820bb3b9a54334f3268fc5ee1e967d2e1c0db34nvdWEB
- github.com/vitejs/vite/commit/8339d7408668686bae56eaccbfdc7b87612904bdghsaWEB
- github.com/vitejs/vite/commit/a6da45082b6e73ddfdcdcc06bb5414f976a388d6ghsaWEB
- github.com/vitejs/vite/commit/b901438f99e667f76662840826eec91c8ab3b3e7ghsaWEB
- github.com/vitejs/vite/security/advisories/GHSA-9cwx-2883-4wfxnvdWEB
News mentions
0No linked articles in our index yet.