CVE-2024-31207
Description
Vite (French word for "quick", pronounced /vit/, like "veet") is a frontend build tooling to improve the frontend development experience.server.fs.deny does not deny requests for patterns with directories. This vulnerability has been patched in version(s) 5.2.6, 5.1.7, 5.0.13, 4.5.3, 3.2.10 and 2.9.18.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
vitenpm | >= 2.7.0, < 2.9.18 | 2.9.18 |
vitenpm | >= 3.0.0, < 3.2.10 | 3.2.10 |
vitenpm | >= 4.0.0, < 4.5.3 | 4.5.3 |
vitenpm | >= 5.0.0, < 5.0.13 | 5.0.13 |
vitenpm | >= 5.1.0, < 5.1.7 | 5.1.7 |
vitenpm | >= 5.2.0, < 5.2.6 | 5.2.6 |
Patches
6011bbca350e4fix: port #16250 to v2 (#16254)
7 files changed · +53 −4
packages/playground/fs-serve/package.json+4 −1 modified@@ -6,6 +6,9 @@ "dev": "vite root", "build": "vite build root", "debug": "node --inspect-brk ../../vite/bin/vite", - "preview": "vite preview" + "preview": "vite preview", + "dev:deny": "vite root --config ./root/vite.config-deny.js", + "build:deny": "vite build root --config ./root/vite.config-deny.js", + "preview:deny": "vite preview root --config ./root/vite.config-deny.js" } }
packages/playground/fs-serve/root/src/deny/.deny+1 −0 added@@ -0,0 +1 @@ +.deny
packages/playground/fs-serve/root/src/deny/deny.txt+1 −0 added@@ -0,0 +1 @@ +deny
packages/playground/fs-serve/root/vite.config-deny.js+15 −0 added@@ -0,0 +1,15 @@ +const path = require('path') +const { defineConfig } = require('vite') + +module.exports = defineConfig({ + server: { + fs: { + strict: true, + allow: [path.resolve(__dirname, 'src')], + deny: ['**/deny/**'] + } + }, + define: { + ROOT: JSON.stringify(path.dirname(__dirname).replace(/\\/g, '/')) + } +})
packages/playground/fs-serve/__tests__/deny/fs-serve-deny.spec.ts+22 −0 added@@ -0,0 +1,22 @@ +import { isBuild } from '../../../testUtils' + +describe('main', () => { + if (!isBuild) { + test('**/deny/** should deny src/deny/deny.txt', async () => { + const res = await page.request.fetch( + new URL('/src/deny/deny.txt', viteTestUrl).href + ) + expect(res.status()).toBe(403) + }) + test('**/deny/** should deny src/deny/.deny', async () => { + const res = await page.request.fetch( + new URL('/src/deny/.deny', viteTestUrl).href + ) + expect(res.status()).toBe(403) + }) + } else { + test('dummy test to make jest happy', async () => { + // Your test suite must contain at least one test. + }) + } +})
packages/playground/fs-serve/__tests__/deny/vite.config.js+1 −0 added@@ -0,0 +1 @@ +module.exports = require('../../root/vite.config-deny')
packages/vite/src/node/server/middlewares/static.ts+9 −3 modified@@ -156,7 +156,11 @@ export function serveRawFsMiddleware( } } -const _matchOptions = { matchBase: true, nocase: true } +const _matchOptions = { + matchBase: false, + nocase: true, + dot: true +} export function isFileServingAllowed( url: string, @@ -166,8 +170,10 @@ export function isFileServingAllowed( const file = fsPathFromUrl(url) - if (server.config.server.fs.deny.some((i) => isMatch(file, i, _matchOptions))) - return false + const deny = server.config.server.fs.deny.map((pattern) => + pattern.includes('/') ? pattern : `**/${pattern}` + ) + if (deny.some((i) => isMatch(file, i, _matchOptions))) return false if (server.moduleGraph.safeModulesPath.has(file)) return true
89c7c645f09dfix: port #16250 to v3 (#16253)
7 files changed · +59 −5
packages/vite/src/node/server/index.ts+13 −4 modified@@ -452,10 +452,19 @@ export async function createServer( _importGlobMap: new Map(), _forceOptimizeOnRestart: false, _pendingRequests: new Map(), - _fsDenyGlob: picomatch(config.server.fs.deny, { - matchBase: true, - nocase: true - }) + _fsDenyGlob: picomatch( + // matchBase: true does not work as it's documented + // https://github.com/micromatch/picomatch/issues/89 + // convert patterns without `/` on our side for now + config.server.fs.deny.map((pattern) => + pattern.includes('/') ? pattern : `**/${pattern}` + ), + { + matchBase: false, + nocase: true, + dot: true + } + ) } server.transformIndexHtml = createDevHtmlTransformFn(server)
playground/fs-serve/package.json+4 −1 modified@@ -6,6 +6,9 @@ "dev": "vite root", "build": "vite build root", "debug": "node --inspect-brk ../../packages/vite/bin/vite", - "preview": "vite preview root" + "preview": "vite preview root", + "dev:deny": "vite root --config ./root/vite.config-deny.js", + "build:deny": "vite build root --config ./root/vite.config-deny.js", + "preview:deny": "vite preview root --config ./root/vite.config-deny.js" } }
playground/fs-serve/root/src/deny/.deny+1 −0 added@@ -0,0 +1 @@ +.deny
playground/fs-serve/root/src/deny/deny.txt+1 −0 added@@ -0,0 +1 @@ +deny
playground/fs-serve/root/vite.config-deny.js+22 −0 added@@ -0,0 +1,22 @@ +import path from 'node:path' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + rollupOptions: { + input: { + main: path.resolve(__dirname, 'src/index.html') + } + } + }, + server: { + fs: { + strict: true, + allow: [path.resolve(__dirname, 'src')], + deny: ['**/deny/**'] + } + }, + define: { + ROOT: JSON.stringify(path.dirname(__dirname).replace(/\\/g, '/')) + } +})
playground/fs-serve/__tests__/deny/fs-serve-deny.spec.ts+17 −0 added@@ -0,0 +1,17 @@ +import { describe, expect, test } from 'vitest' +import { isServe, page, viteTestUrl } from '~utils' + +describe.runIf(isServe)('main', () => { + test('**/deny/** should deny src/deny/deny.txt', async () => { + const res = await page.request.fetch( + new URL('/src/deny/deny.txt', viteTestUrl).href + ) + expect(res.status()).toBe(403) + }) + test('**/deny/** should deny src/deny/.deny', async () => { + const res = await page.request.fetch( + new URL('/src/deny/.deny', viteTestUrl).href + ) + expect(res.status()).toBe(403) + }) +})
playground/fs-serve/__tests__/deny/vite.config.js+1 −0 added@@ -0,0 +1 @@ +module.exports = require('../../root/vite.config-deny')
5a056dd2fc80fix: `fs.deny` with globs with directories (#16250)
6 files changed · +58 −5
packages/vite/src/node/server/index.ts+13 −4 modified@@ -646,10 +646,19 @@ export async function _createServer( _importGlobMap: new Map(), _forceOptimizeOnRestart: false, _pendingRequests: new Map(), - _fsDenyGlob: picomatch(config.server.fs.deny, { - matchBase: true, - nocase: true, - }), + _fsDenyGlob: picomatch( + // matchBase: true does not work as it's documented + // https://github.com/micromatch/picomatch/issues/89 + // convert patterns without `/` on our side for now + config.server.fs.deny.map((pattern) => + pattern.includes('/') ? pattern : `**/${pattern}`, + ), + { + matchBase: false, + nocase: true, + dot: true, + }, + ), _shortcutsOptions: undefined, }
playground/fs-serve/package.json+4 −1 modified@@ -10,6 +10,9 @@ "preview": "vite preview root", "dev:base": "vite root --config ./root/vite.config-base.js", "build:base": "vite build root --config ./root/vite.config-base.js", - "preview:base": "vite preview root --config ./root/vite.config-base.js" + "preview:base": "vite preview root --config ./root/vite.config-base.js", + "dev:deny": "vite root --config ./root/vite.config-deny.js", + "build:deny": "vite build root --config ./root/vite.config-deny.js", + "preview:deny": "vite preview root --config ./root/vite.config-deny.js" } }
playground/fs-serve/root/src/deny/.deny+1 −0 added@@ -0,0 +1 @@ +.deny
playground/fs-serve/root/src/deny/deny.txt+1 −0 added@@ -0,0 +1 @@ +deny
playground/fs-serve/root/vite.config-deny.js+22 −0 added@@ -0,0 +1,22 @@ +import path from 'node:path' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + rollupOptions: { + input: { + main: path.resolve(__dirname, 'src/index.html'), + }, + }, + }, + server: { + fs: { + strict: true, + allow: [path.resolve(__dirname, 'src')], + deny: ['**/deny/**'], + }, + }, + define: { + ROOT: JSON.stringify(path.dirname(__dirname).replace(/\\/g, '/')), + }, +})
playground/fs-serve/__tests__/deny/fs-serve-deny.spec.ts+17 −0 added@@ -0,0 +1,17 @@ +import { describe, expect, test } from 'vitest' +import { isServe, page, viteTestUrl } from '~utils' + +describe.runIf(isServe)('main', () => { + test('**/deny/** should deny src/deny/deny.txt', async () => { + const res = await page.request.fetch( + new URL('/src/deny/deny.txt', viteTestUrl).href, + ) + expect(res.status()).toBe(403) + }) + test('**/deny/** should deny src/deny/.deny', async () => { + const res = await page.request.fetch( + new URL('/src/deny/.deny', viteTestUrl).href, + ) + expect(res.status()).toBe(403) + }) +})
d2db33f7d4b9fix: `fs.deny` with globs with directories (#16250)
6 files changed · +58 −5
packages/vite/src/node/server/index.ts+13 −4 modified@@ -617,10 +617,19 @@ export async function _createServer( _importGlobMap: new Map(), _forceOptimizeOnRestart: false, _pendingRequests: new Map(), - _fsDenyGlob: picomatch(config.server.fs.deny, { - matchBase: true, - nocase: true, - }), + _fsDenyGlob: picomatch( + // matchBase: true does not work as it's documented + // https://github.com/micromatch/picomatch/issues/89 + // convert patterns without `/` on our side for now + config.server.fs.deny.map((pattern) => + pattern.includes('/') ? pattern : `**/${pattern}`, + ), + { + matchBase: false, + nocase: true, + dot: true, + }, + ), _shortcutsOptions: undefined, }
playground/fs-serve/package.json+4 −1 modified@@ -10,6 +10,9 @@ "preview": "vite preview root", "dev:base": "vite root --config ./root/vite.config-base.js", "build:base": "vite build root --config ./root/vite.config-base.js", - "preview:base": "vite preview root --config ./root/vite.config-base.js" + "preview:base": "vite preview root --config ./root/vite.config-base.js", + "dev:deny": "vite root --config ./root/vite.config-deny.js", + "build:deny": "vite build root --config ./root/vite.config-deny.js", + "preview:deny": "vite preview root --config ./root/vite.config-deny.js" } }
playground/fs-serve/root/src/deny/.deny+1 −0 added@@ -0,0 +1 @@ +.deny
playground/fs-serve/root/src/deny/deny.txt+1 −0 added@@ -0,0 +1 @@ +deny
playground/fs-serve/root/vite.config-deny.js+22 −0 added@@ -0,0 +1,22 @@ +import path from 'node:path' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + rollupOptions: { + input: { + main: path.resolve(__dirname, 'src/index.html'), + }, + }, + }, + server: { + fs: { + strict: true, + allow: [path.resolve(__dirname, 'src')], + deny: ['**/deny/**'], + }, + }, + define: { + ROOT: JSON.stringify(path.dirname(__dirname).replace(/\\/g, '/')), + }, +})
playground/fs-serve/__tests__/deny/fs-serve-deny.spec.ts+17 −0 added@@ -0,0 +1,17 @@ +import { describe, expect, test } from 'vitest' +import { isServe, page, viteTestUrl } from '~utils' + +describe.runIf(isServe)('main', () => { + test('**/deny/** should deny src/deny/deny.txt', async () => { + const res = await page.request.fetch( + new URL('/src/deny/deny.txt', viteTestUrl).href, + ) + expect(res.status()).toBe(403) + }) + test('**/deny/** should deny src/deny/.deny', async () => { + const res = await page.request.fetch( + new URL('/src/deny/.deny', viteTestUrl).href, + ) + expect(res.status()).toBe(403) + }) +})
96a7f3a41ef2fix: `fs.deny` with globs with directories (#16250)
6 files changed · +58 −5
packages/vite/src/node/server/index.ts+13 −4 modified@@ -509,10 +509,19 @@ export async function _createServer( _importGlobMap: new Map(), _forceOptimizeOnRestart: false, _pendingRequests: new Map(), - _fsDenyGlob: picomatch(config.server.fs.deny, { - matchBase: true, - nocase: true, - }), + _fsDenyGlob: picomatch( + // matchBase: true does not work as it's documented + // https://github.com/micromatch/picomatch/issues/89 + // convert patterns without `/` on our side for now + config.server.fs.deny.map((pattern) => + pattern.includes('/') ? pattern : `**/${pattern}`, + ), + { + matchBase: false, + nocase: true, + dot: true, + }, + ), _shortcutsOptions: undefined, }
playground/fs-serve/package.json+4 −1 modified@@ -10,6 +10,9 @@ "preview": "vite preview root", "dev:base": "vite root --config ./root/vite.config-base.js", "build:base": "vite build root --config ./root/vite.config-base.js", - "preview:base": "vite preview root --config ./root/vite.config-base.js" + "preview:base": "vite preview root --config ./root/vite.config-base.js", + "dev:deny": "vite root --config ./root/vite.config-deny.js", + "build:deny": "vite build root --config ./root/vite.config-deny.js", + "preview:deny": "vite preview root --config ./root/vite.config-deny.js" } }
playground/fs-serve/root/src/deny/.deny+1 −0 added@@ -0,0 +1 @@ +.deny
playground/fs-serve/root/src/deny/deny.txt+1 −0 added@@ -0,0 +1 @@ +deny
playground/fs-serve/root/vite.config-deny.js+22 −0 added@@ -0,0 +1,22 @@ +import path from 'node:path' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + rollupOptions: { + input: { + main: path.resolve(__dirname, 'src/index.html'), + }, + }, + }, + server: { + fs: { + strict: true, + allow: [path.resolve(__dirname, 'src')], + deny: ['**/deny/**'], + }, + }, + define: { + ROOT: JSON.stringify(path.dirname(__dirname).replace(/\\/g, '/')), + }, +})
playground/fs-serve/__tests__/deny/fs-serve-deny.spec.ts+17 −0 added@@ -0,0 +1,17 @@ +import { describe, expect, test } from 'vitest' +import { isServe, page, viteTestUrl } from '~utils' + +describe.runIf(isServe)('main', () => { + test('**/deny/** should deny src/deny/deny.txt', async () => { + const res = await page.request.fetch( + new URL('/src/deny/deny.txt', viteTestUrl).href, + ) + expect(res.status()).toBe(403) + }) + test('**/deny/** should deny src/deny/.deny', async () => { + const res = await page.request.fetch( + new URL('/src/deny/.deny', viteTestUrl).href, + ) + expect(res.status()).toBe(403) + }) +})
ba5269cca81dfix: `fs.deny` with globs with directories (#16250)
6 files changed · +58 −5
packages/vite/src/node/server/index.ts+13 −4 modified@@ -685,10 +685,19 @@ export async function _createServer( _importGlobMap: new Map(), _forceOptimizeOnRestart: false, _pendingRequests: new Map(), - _fsDenyGlob: picomatch(config.server.fs.deny, { - matchBase: true, - nocase: true, - }), + _fsDenyGlob: picomatch( + // matchBase: true does not work as it's documented + // https://github.com/micromatch/picomatch/issues/89 + // convert patterns without `/` on our side for now + config.server.fs.deny.map((pattern) => + pattern.includes('/') ? pattern : `**/${pattern}`, + ), + { + matchBase: false, + nocase: true, + dot: true, + }, + ), _shortcutsOptions: undefined, }
playground/fs-serve/package.json+4 −1 modified@@ -10,6 +10,9 @@ "preview": "vite preview root", "dev:base": "vite root --config ./root/vite.config-base.js", "build:base": "vite build root --config ./root/vite.config-base.js", - "preview:base": "vite preview root --config ./root/vite.config-base.js" + "preview:base": "vite preview root --config ./root/vite.config-base.js", + "dev:deny": "vite root --config ./root/vite.config-deny.js", + "build:deny": "vite build root --config ./root/vite.config-deny.js", + "preview:deny": "vite preview root --config ./root/vite.config-deny.js" } }
playground/fs-serve/root/src/deny/.deny+1 −0 added@@ -0,0 +1 @@ +.deny
playground/fs-serve/root/src/deny/deny.txt+1 −0 added@@ -0,0 +1 @@ +deny
playground/fs-serve/root/vite.config-deny.js+22 −0 added@@ -0,0 +1,22 @@ +import path from 'node:path' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + rollupOptions: { + input: { + main: path.resolve(__dirname, 'src/index.html'), + }, + }, + }, + server: { + fs: { + strict: true, + allow: [path.resolve(__dirname, 'src')], + deny: ['**/deny/**'], + }, + }, + define: { + ROOT: JSON.stringify(path.dirname(__dirname).replace(/\\/g, '/')), + }, +})
playground/fs-serve/__tests__/deny/fs-serve-deny.spec.ts+17 −0 added@@ -0,0 +1,17 @@ +import { describe, expect, test } from 'vitest' +import { isServe, page, viteTestUrl } from '~utils' + +describe.runIf(isServe)('main', () => { + test('**/deny/** should deny src/deny/deny.txt', async () => { + const res = await page.request.fetch( + new URL('/src/deny/deny.txt', viteTestUrl).href, + ) + expect(res.status()).toBe(403) + }) + test('**/deny/** should deny src/deny/.deny', async () => { + const res = await page.request.fetch( + new URL('/src/deny/.deny', viteTestUrl).href, + ) + expect(res.status()).toBe(403) + }) +})
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
9- github.com/advisories/GHSA-8jhw-289h-jh2gghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-31207ghsaADVISORY
- github.com/vitejs/vite/commit/011bbca350e447d1b499d242804ce62738c12bc0nvdWEB
- github.com/vitejs/vite/commit/5a056dd2fc80dbafed033062fe6aaf4717309f48nvdWEB
- github.com/vitejs/vite/commit/89c7c645f09d16a38f146ef4a1528f218e844d67nvdWEB
- github.com/vitejs/vite/commit/96a7f3a41ef2f9351c46f3ab12489bb4efa03cc9nvdWEB
- github.com/vitejs/vite/commit/ba5269cca81de3f5fbb0f49d58a1c55688043258nvdWEB
- github.com/vitejs/vite/commit/d2db33f7d4b96750b35370c70dd2c35ec3b9b649nvdWEB
- github.com/vitejs/vite/security/advisories/GHSA-8jhw-289h-jh2gnvdWEB
News mentions
0No linked articles in our index yet.