Vite middleware may serve files starting with the same name with the public directory
Description
Vite is a frontend tooling framework for JavaScript. Prior to versions 7.1.5, 7.0.7, 6.3.6, and 5.4.20, files starting with the same name with the public directory were served bypassing the server.fs settings. Only apps that explicitly expose the Vite dev server to the network (using --host or server.host config option), use the public directory feature (enabled by default), and have a symlink in the public directory are affected. Versions 7.1.5, 7.0.7, 6.3.6, and 5.4.20 fix the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
vitenpm | >= 7.1.0, < 7.1.5 | 7.1.5 |
vitenpm | >= 7.0.0, < 7.0.7 | 7.0.7 |
vitenpm | >= 6.0.0, < 6.3.6 | 6.3.6 |
vitenpm | < 5.4.20 | 5.4.20 |
Affected products
1Patches
54f1c35bcbb58fix: port sirv@3.0.2 changes to sirv@2.0.4 (#20737)
2 files changed · +38 −6
patches/sirv@2.0.4.patch+34 −2 modified@@ -1,7 +1,14 @@ diff --git a/build.js b/build.js -index 0884d9ffaa24d0ae3ec020a6faff12b3125b2071..85b82c90346e829bbf723e913f282c80096df061 100644 +index 0884d9ffaa24d0ae3ec020a6faff12b3125b2071..0e2a690abb3bd121f6ba7c0855163b816f5a7a70 100644 --- a/build.js +++ b/build.js +@@ -1,5 +1,5 @@ + const fs = require('fs'); +-const { join, normalize, resolve } = require('path'); ++const { join, normalize, resolve, sep } = require('path'); + const { totalist } = require('totalist/sync'); + const { parse } = require('@polka/url'); + const { lookup } = require('mrmime'); @@ -35,7 +35,7 @@ function viaCache(cache, uri, extns) { } } @@ -19,6 +26,15 @@ index 0884d9ffaa24d0ae3ec020a6faff12b3125b2071..85b82c90346e829bbf723e913f282c80 headers = toHeaders(name, stats, isEtag); headers['Cache-Control'] = isEtag ? 'no-cache' : 'no-store'; return { abs, stats, headers }; +@@ -161,7 +162,7 @@ module.exports = function (dir, opts={}) { + }); + } + +- let lookup = opts.dev ? viaLocal.bind(0, dir, isEtag) : viaCache.bind(0, FILES); ++ let lookup = opts.dev ? viaLocal.bind(0, dir.endsWith(sep) ? dir : dir + sep, isEtag) : viaCache.bind(0, FILES); + + return function (req, res, next) { + let extns = ['']; @@ -176,7 +177,7 @@ module.exports = function (dir, opts={}) { catch (err) { /* malform uri */ } } @@ -29,9 +45,16 @@ index 0884d9ffaa24d0ae3ec020a6faff12b3125b2071..85b82c90346e829bbf723e913f282c80 if (isEtag && req.headers['if-none-match'] === data.headers['ETag']) { diff --git a/build.mjs b/build.mjs -index c93bbe6bdfb7ad13ee20f0c44d80d6aacdd64087..3dc3e22f09abcae51aef7b75c34dc08b1f6e6abd 100644 +index c93bbe6bdfb7ad13ee20f0c44d80d6aacdd64087..9127789c792ffb23022524fd2a6f997286afe0cf 100644 --- a/build.mjs +++ b/build.mjs +@@ -1,5 +1,5 @@ + import * as fs from 'fs'; +-import { join, normalize, resolve } from 'path'; ++import { join, normalize, resolve, sep } from 'path'; + import { totalist } from 'totalist/sync'; + import { parse } from '@polka/url'; + import { lookup } from 'mrmime'; @@ -35,7 +35,7 @@ function viaCache(cache, uri, extns) { } } @@ -49,6 +72,15 @@ index c93bbe6bdfb7ad13ee20f0c44d80d6aacdd64087..3dc3e22f09abcae51aef7b75c34dc08b headers = toHeaders(name, stats, isEtag); headers['Cache-Control'] = isEtag ? 'no-cache' : 'no-store'; return { abs, stats, headers }; +@@ -161,7 +162,7 @@ export default function (dir, opts={}) { + }); + } + +- let lookup = opts.dev ? viaLocal.bind(0, dir, isEtag) : viaCache.bind(0, FILES); ++ let lookup = opts.dev ? viaLocal.bind(0, dir.endsWith(sep) ? dir : dir + sep, isEtag) : viaCache.bind(0, FILES); + + return function (req, res, next) { + let extns = ['']; @@ -176,7 +177,7 @@ export default function (dir, opts={}) { catch (err) { /* malform uri */ } }
pnpm-lock.yaml+4 −4 modified@@ -18,7 +18,7 @@ patchedDependencies: hash: qqiqxx62zlcu62nljjmhlvexni path: patches/http-proxy@1.18.1.patch sirv@2.0.4: - hash: amdes53ifqfntejkflpaq5ifce + hash: owsaynqbfztzqsvxv437uugt5q path: patches/sirv@2.0.4.patch importers: @@ -390,7 +390,7 @@ importers: version: 1.77.8 sirv: specifier: ^2.0.4 - version: 2.0.4(patch_hash=amdes53ifqfntejkflpaq5ifce) + version: 2.0.4(patch_hash=owsaynqbfztzqsvxv437uugt5q) source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -802,7 +802,7 @@ importers: devDependencies: sirv: specifier: ^2.0.4 - version: 2.0.4(patch_hash=amdes53ifqfntejkflpaq5ifce) + version: 2.0.4(patch_hash=owsaynqbfztzqsvxv437uugt5q) playground/minify: dependencies: @@ -12438,7 +12438,7 @@ snapshots: simple-git-hooks@2.11.1: {} - sirv@2.0.4(patch_hash=amdes53ifqfntejkflpaq5ifce): + sirv@2.0.4(patch_hash=owsaynqbfztzqsvxv437uugt5q): dependencies: '@polka/url': 1.0.0-next.24 mrmime: 2.0.0
6 files changed · +40 −22
package.json+1 −1 modified@@ -102,7 +102,7 @@ }, "patchedDependencies": { "http-proxy@1.18.1": "patches/http-proxy@1.18.1.patch", - "sirv@3.0.1": "patches/sirv@3.0.1.patch", + "sirv@3.0.2": "patches/sirv@3.0.2.patch", "chokidar@3.6.0": "patches/chokidar@3.6.0.patch", "dotenv-expand@12.0.2": "patches/dotenv-expand@12.0.2.patch" },
packages/vite/package.json+1 −1 modified@@ -144,7 +144,7 @@ "rollup-plugin-license": "^3.6.0", "sass": "^1.86.3", "sass-embedded": "^1.86.3", - "sirv": "^3.0.1", + "sirv": "^3.0.2", "source-map-support": "^0.5.21", "strip-literal": "^3.0.0", "terser": "^5.39.0",
patches/sirv@3.0.2.patch+24 −6 renamed@@ -1,5 +1,5 @@ diff --git a/build.js b/build.js -index 0c6ed8e2c0949c93978dd1a244baa9bf2448e9b8..08a9347cfdca06e6a97077ea4582c5b0922ecb2d 100644 +index 3734120d67745ff83b2df07fa5d0a40dcb92a69b..bd57f693ac2bd4555a12e7a5436fb9524789ef66 100644 --- a/build.js +++ b/build.js @@ -35,7 +35,7 @@ function viaCache(cache, uri, extns) { @@ -11,15 +11,24 @@ index 0c6ed8e2c0949c93978dd1a244baa9bf2448e9b8..08a9347cfdca06e6a97077ea4582c5b0 let i=0, arr=toAssume(uri, extns); let abs, stats, name, headers; for (; i < arr.length; i++) { -@@ -43,6 +43,7 @@ function viaLocal(dir, isEtag, uri, extns) { +@@ -46,6 +46,7 @@ function viaLocal(dir, isEtag, uri, extns) { if (abs.startsWith(dir) && fs.existsSync(abs)) { stats = fs.statSync(abs); if (stats.isDirectory()) continue; + if (shouldServe && !shouldServe(abs)) continue; headers = toHeaders(name, stats, isEtag); headers['Cache-Control'] = isEtag ? 'no-cache' : 'no-store'; return { abs, stats, headers }; -@@ -176,7 +177,7 @@ module.exports = function (dir, opts={}) { +@@ -164,7 +165,7 @@ module.exports = function (dir, opts={}) { + }); + } + +- let lookup = opts.dev ? viaLocal.bind(0, dir + sep, isEtag) : viaCache.bind(0, FILES); ++ let lookup = opts.dev ? viaLocal.bind(0, dir.endsWith(sep) ? dir : dir + sep, isEtag) : viaCache.bind(0, FILES); + + return function (req, res, next) { + let extns = ['']; +@@ -179,7 +180,7 @@ module.exports = function (dir, opts={}) { catch (err) { /* malform uri */ } } @@ -29,7 +38,7 @@ index 0c6ed8e2c0949c93978dd1a244baa9bf2448e9b8..08a9347cfdca06e6a97077ea4582c5b0 if (isEtag && req.headers['if-none-match'] === data.headers['ETag']) { diff --git a/build.mjs b/build.mjs -index 3ad14d45630a8627b93842a04a96465120d3f223..8451277c015b56a7d2cb99aaee3a318d9c0893dd 100644 +index 2f866d5216c951ec125f2044af070fa6b530e375..d5335bfbb16e23b57385c1a83226611e29c39093 100644 --- a/build.mjs +++ b/build.mjs @@ -35,7 +35,7 @@ function viaCache(cache, uri, extns) { @@ -41,15 +50,24 @@ index 3ad14d45630a8627b93842a04a96465120d3f223..8451277c015b56a7d2cb99aaee3a318d let i=0, arr=toAssume(uri, extns); let abs, stats, name, headers; for (; i < arr.length; i++) { -@@ -43,6 +43,7 @@ function viaLocal(dir, isEtag, uri, extns) { +@@ -46,6 +46,7 @@ function viaLocal(dir, isEtag, uri, extns) { if (abs.startsWith(dir) && fs.existsSync(abs)) { stats = fs.statSync(abs); if (stats.isDirectory()) continue; + if (shouldServe && !shouldServe(abs)) continue; headers = toHeaders(name, stats, isEtag); headers['Cache-Control'] = isEtag ? 'no-cache' : 'no-store'; return { abs, stats, headers }; -@@ -176,7 +177,7 @@ export default function (dir, opts={}) { +@@ -164,7 +165,7 @@ export default function (dir, opts={}) { + }); + } + +- let lookup = opts.dev ? viaLocal.bind(0, dir + sep, isEtag) : viaCache.bind(0, FILES); ++ let lookup = opts.dev ? viaLocal.bind(0, dir.endsWith(sep) ? dir : dir + sep, isEtag) : viaCache.bind(0, FILES); + + return function (req, res, next) { + let extns = ['']; +@@ -179,7 +180,7 @@ export default function (dir, opts={}) { catch (err) { /* malform uri */ } }
playground/lib/package.json+1 −1 modified@@ -10,6 +10,6 @@ "preview": "vite preview" }, "devDependencies": { - "sirv": "^3.0.1" + "sirv": "^3.0.2" } }
playground/ssr-conditions/package.json+1 −1 modified@@ -17,6 +17,6 @@ }, "devDependencies": { "express": "^5.1.0", - "sirv": "^3.0.1" + "sirv": "^3.0.2" } }
pnpm-lock.yaml+12 −12 modified@@ -19,9 +19,9 @@ patchedDependencies: http-proxy@1.18.1: hash: 8071c23044f455271f4d4074ae4c7b81beec17a03aefd158d5f4edd4ef751c11 path: patches/http-proxy@1.18.1.patch - sirv@3.0.1: - hash: 95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1 - path: patches/sirv@3.0.1.patch + sirv@3.0.2: + hash: c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95 + path: patches/sirv@3.0.2.patch importers: @@ -387,8 +387,8 @@ importers: specifier: ^1.86.3 version: 1.86.3(source-map-js@1.2.1) sirv: - specifier: ^3.0.1 - version: 3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1) + specifier: ^3.0.2 + version: 3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95) source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -858,8 +858,8 @@ importers: playground/lib: devDependencies: sirv: - specifier: ^3.0.1 - version: 3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1) + specifier: ^3.0.2 + version: 3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95) playground/minify: dependencies: @@ -1393,8 +1393,8 @@ importers: specifier: ^5.1.0 version: 5.1.0 sirv: - specifier: ^3.0.1 - version: 3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1) + specifier: ^3.0.2 + version: 3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95) playground/ssr-conditions/external: {} @@ -6929,8 +6929,8 @@ packages: resolution: {integrity: sha512-NB3V4XyCOrWTIhjh85DyEoVlM3adHWwqQXKYHmuegy/108bJPP6YxuPGm4ZKBq1+GVKRbKJuzNY//09cMJYp+A==} hasBin: true - sirv@3.0.1: - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} sisteransi@1.0.5: @@ -13065,7 +13065,7 @@ snapshots: simple-git-hooks@2.12.1: {} - sirv@3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1): + sirv@3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95): dependencies: '@polka/url': 1.0.0-next.28 mrmime: 2.0.1
6 files changed · +40 −22
packages/vite/package.json+1 −1 modified@@ -143,7 +143,7 @@ "rollup-plugin-license": "^3.6.0", "sass": "^1.92.1", "sass-embedded": "^1.92.1", - "sirv": "^3.0.1", + "sirv": "^3.0.2", "strip-literal": "^3.0.0", "terser": "^5.44.0", "tsconfck": "^3.1.6",
patches/sirv@3.0.2.patch+24 −6 renamed@@ -1,5 +1,5 @@ diff --git a/build.js b/build.js -index 0c6ed8e2c0949c93978dd1a244baa9bf2448e9b8..08a9347cfdca06e6a97077ea4582c5b0922ecb2d 100644 +index 3734120d67745ff83b2df07fa5d0a40dcb92a69b..bd57f693ac2bd4555a12e7a5436fb9524789ef66 100644 --- a/build.js +++ b/build.js @@ -35,7 +35,7 @@ function viaCache(cache, uri, extns) { @@ -11,15 +11,24 @@ index 0c6ed8e2c0949c93978dd1a244baa9bf2448e9b8..08a9347cfdca06e6a97077ea4582c5b0 let i=0, arr=toAssume(uri, extns); let abs, stats, name, headers; for (; i < arr.length; i++) { -@@ -43,6 +43,7 @@ function viaLocal(dir, isEtag, uri, extns) { +@@ -46,6 +46,7 @@ function viaLocal(dir, isEtag, uri, extns) { if (abs.startsWith(dir) && fs.existsSync(abs)) { stats = fs.statSync(abs); if (stats.isDirectory()) continue; + if (shouldServe && !shouldServe(abs)) continue; headers = toHeaders(name, stats, isEtag); headers['Cache-Control'] = isEtag ? 'no-cache' : 'no-store'; return { abs, stats, headers }; -@@ -176,7 +177,7 @@ module.exports = function (dir, opts={}) { +@@ -164,7 +165,7 @@ module.exports = function (dir, opts={}) { + }); + } + +- let lookup = opts.dev ? viaLocal.bind(0, dir + sep, isEtag) : viaCache.bind(0, FILES); ++ let lookup = opts.dev ? viaLocal.bind(0, dir.endsWith(sep) ? dir : dir + sep, isEtag) : viaCache.bind(0, FILES); + + return function (req, res, next) { + let extns = ['']; +@@ -179,7 +180,7 @@ module.exports = function (dir, opts={}) { catch (err) { /* malform uri */ } } @@ -29,7 +38,7 @@ index 0c6ed8e2c0949c93978dd1a244baa9bf2448e9b8..08a9347cfdca06e6a97077ea4582c5b0 if (isEtag && req.headers['if-none-match'] === data.headers['ETag']) { diff --git a/build.mjs b/build.mjs -index 3ad14d45630a8627b93842a04a96465120d3f223..8451277c015b56a7d2cb99aaee3a318d9c0893dd 100644 +index 2f866d5216c951ec125f2044af070fa6b530e375..d5335bfbb16e23b57385c1a83226611e29c39093 100644 --- a/build.mjs +++ b/build.mjs @@ -35,7 +35,7 @@ function viaCache(cache, uri, extns) { @@ -41,15 +50,24 @@ index 3ad14d45630a8627b93842a04a96465120d3f223..8451277c015b56a7d2cb99aaee3a318d let i=0, arr=toAssume(uri, extns); let abs, stats, name, headers; for (; i < arr.length; i++) { -@@ -43,6 +43,7 @@ function viaLocal(dir, isEtag, uri, extns) { +@@ -46,6 +46,7 @@ function viaLocal(dir, isEtag, uri, extns) { if (abs.startsWith(dir) && fs.existsSync(abs)) { stats = fs.statSync(abs); if (stats.isDirectory()) continue; + if (shouldServe && !shouldServe(abs)) continue; headers = toHeaders(name, stats, isEtag); headers['Cache-Control'] = isEtag ? 'no-cache' : 'no-store'; return { abs, stats, headers }; -@@ -176,7 +177,7 @@ export default function (dir, opts={}) { +@@ -164,7 +165,7 @@ export default function (dir, opts={}) { + }); + } + +- let lookup = opts.dev ? viaLocal.bind(0, dir + sep, isEtag) : viaCache.bind(0, FILES); ++ let lookup = opts.dev ? viaLocal.bind(0, dir.endsWith(sep) ? dir : dir + sep, isEtag) : viaCache.bind(0, FILES); + + return function (req, res, next) { + let extns = ['']; +@@ -179,7 +180,7 @@ export default function (dir, opts={}) { catch (err) { /* malform uri */ } }
playground/lib/package.json+1 −1 modified@@ -10,6 +10,6 @@ "preview": "vite preview" }, "devDependencies": { - "sirv": "^3.0.1" + "sirv": "^3.0.2" } }
playground/ssr-conditions/package.json+1 −1 modified@@ -17,6 +17,6 @@ }, "devDependencies": { "express": "^5.1.0", - "sirv": "^3.0.1" + "sirv": "^3.0.2" } }
pnpm-lock.yaml+12 −12 modified@@ -16,9 +16,9 @@ patchedDependencies: dotenv-expand@12.0.3: hash: 49330a663821151418e003e822a82a6a61d2f0f8a6e3cab00c1c94815a112889 path: patches/dotenv-expand@12.0.3.patch - sirv@3.0.1: - hash: 95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1 - path: patches/sirv@3.0.1.patch + sirv@3.0.2: + hash: c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95 + path: patches/sirv@3.0.2.patch importers: @@ -405,8 +405,8 @@ importers: specifier: ^1.92.1 version: 1.92.1(source-map-js@1.2.1) sirv: - specifier: ^3.0.1 - version: 3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1) + specifier: ^3.0.2 + version: 3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95) strip-literal: specifier: ^3.0.0 version: 3.0.0 @@ -878,8 +878,8 @@ importers: playground/lib: devDependencies: sirv: - specifier: ^3.0.1 - version: 3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1) + specifier: ^3.0.2 + version: 3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95) playground/minify: dependencies: @@ -1428,8 +1428,8 @@ importers: specifier: ^5.1.0 version: 5.1.0 sirv: - specifier: ^3.0.1 - version: 3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1) + specifier: ^3.0.2 + version: 3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95) playground/ssr-conditions/external: {} @@ -6780,8 +6780,8 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - sirv@3.0.1: - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} sisteransi@1.0.5: @@ -12792,7 +12792,7 @@ snapshots: dependencies: is-arrayish: 0.3.2 - sirv@3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1): + sirv@3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95): dependencies: '@polka/url': 1.0.0-next.28 mrmime: 2.0.1
pnpm-workspace.yaml+1 −1 modified@@ -14,7 +14,7 @@ dedupeInjectedDeps: false overrides: vite: 'workspace:*' patchedDependencies: - "sirv@3.0.1": "patches/sirv@3.0.1.patch" + "sirv@3.0.2": "patches/sirv@3.0.2.patch" "chokidar@3.6.0": "patches/chokidar@3.6.0.patch" "dotenv-expand@12.0.3": "patches/dotenv-expand@12.0.3.patch" peerDependencyRules:
6 files changed · +40 −22
packages/vite/package.json+1 −1 modified@@ -142,7 +142,7 @@ "rollup-plugin-license": "^3.6.0", "sass": "^1.89.2", "sass-embedded": "^1.89.2", - "sirv": "^3.0.1", + "sirv": "^3.0.2", "strip-literal": "^3.0.0", "terser": "^5.43.1", "tsconfck": "^3.1.6",
patches/sirv@3.0.2.patch+24 −6 renamed@@ -1,5 +1,5 @@ diff --git a/build.js b/build.js -index 0c6ed8e2c0949c93978dd1a244baa9bf2448e9b8..08a9347cfdca06e6a97077ea4582c5b0922ecb2d 100644 +index 3734120d67745ff83b2df07fa5d0a40dcb92a69b..bd57f693ac2bd4555a12e7a5436fb9524789ef66 100644 --- a/build.js +++ b/build.js @@ -35,7 +35,7 @@ function viaCache(cache, uri, extns) { @@ -11,15 +11,24 @@ index 0c6ed8e2c0949c93978dd1a244baa9bf2448e9b8..08a9347cfdca06e6a97077ea4582c5b0 let i=0, arr=toAssume(uri, extns); let abs, stats, name, headers; for (; i < arr.length; i++) { -@@ -43,6 +43,7 @@ function viaLocal(dir, isEtag, uri, extns) { +@@ -46,6 +46,7 @@ function viaLocal(dir, isEtag, uri, extns) { if (abs.startsWith(dir) && fs.existsSync(abs)) { stats = fs.statSync(abs); if (stats.isDirectory()) continue; + if (shouldServe && !shouldServe(abs)) continue; headers = toHeaders(name, stats, isEtag); headers['Cache-Control'] = isEtag ? 'no-cache' : 'no-store'; return { abs, stats, headers }; -@@ -176,7 +177,7 @@ module.exports = function (dir, opts={}) { +@@ -164,7 +165,7 @@ module.exports = function (dir, opts={}) { + }); + } + +- let lookup = opts.dev ? viaLocal.bind(0, dir + sep, isEtag) : viaCache.bind(0, FILES); ++ let lookup = opts.dev ? viaLocal.bind(0, dir.endsWith(sep) ? dir : dir + sep, isEtag) : viaCache.bind(0, FILES); + + return function (req, res, next) { + let extns = ['']; +@@ -179,7 +180,7 @@ module.exports = function (dir, opts={}) { catch (err) { /* malform uri */ } } @@ -29,7 +38,7 @@ index 0c6ed8e2c0949c93978dd1a244baa9bf2448e9b8..08a9347cfdca06e6a97077ea4582c5b0 if (isEtag && req.headers['if-none-match'] === data.headers['ETag']) { diff --git a/build.mjs b/build.mjs -index 3ad14d45630a8627b93842a04a96465120d3f223..8451277c015b56a7d2cb99aaee3a318d9c0893dd 100644 +index 2f866d5216c951ec125f2044af070fa6b530e375..d5335bfbb16e23b57385c1a83226611e29c39093 100644 --- a/build.mjs +++ b/build.mjs @@ -35,7 +35,7 @@ function viaCache(cache, uri, extns) { @@ -41,15 +50,24 @@ index 3ad14d45630a8627b93842a04a96465120d3f223..8451277c015b56a7d2cb99aaee3a318d let i=0, arr=toAssume(uri, extns); let abs, stats, name, headers; for (; i < arr.length; i++) { -@@ -43,6 +43,7 @@ function viaLocal(dir, isEtag, uri, extns) { +@@ -46,6 +46,7 @@ function viaLocal(dir, isEtag, uri, extns) { if (abs.startsWith(dir) && fs.existsSync(abs)) { stats = fs.statSync(abs); if (stats.isDirectory()) continue; + if (shouldServe && !shouldServe(abs)) continue; headers = toHeaders(name, stats, isEtag); headers['Cache-Control'] = isEtag ? 'no-cache' : 'no-store'; return { abs, stats, headers }; -@@ -176,7 +177,7 @@ export default function (dir, opts={}) { +@@ -164,7 +165,7 @@ export default function (dir, opts={}) { + }); + } + +- let lookup = opts.dev ? viaLocal.bind(0, dir + sep, isEtag) : viaCache.bind(0, FILES); ++ let lookup = opts.dev ? viaLocal.bind(0, dir.endsWith(sep) ? dir : dir + sep, isEtag) : viaCache.bind(0, FILES); + + return function (req, res, next) { + let extns = ['']; +@@ -179,7 +180,7 @@ export default function (dir, opts={}) { catch (err) { /* malform uri */ } }
playground/lib/package.json+1 −1 modified@@ -10,6 +10,6 @@ "preview": "vite preview" }, "devDependencies": { - "sirv": "^3.0.1" + "sirv": "^3.0.2" } }
playground/ssr-conditions/package.json+1 −1 modified@@ -17,6 +17,6 @@ }, "devDependencies": { "express": "^5.1.0", - "sirv": "^3.0.1" + "sirv": "^3.0.2" } }
pnpm-lock.yaml+12 −12 modified@@ -19,9 +19,9 @@ patchedDependencies: http-proxy@1.18.1: hash: 8071c23044f455271f4d4074ae4c7b81beec17a03aefd158d5f4edd4ef751c11 path: patches/http-proxy@1.18.1.patch - sirv@3.0.1: - hash: 95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1 - path: patches/sirv@3.0.1.patch + sirv@3.0.2: + hash: c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95 + path: patches/sirv@3.0.2.patch importers: @@ -399,8 +399,8 @@ importers: specifier: ^1.89.2 version: 1.89.2(source-map-js@1.2.1) sirv: - specifier: ^3.0.1 - version: 3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1) + specifier: ^3.0.2 + version: 3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95) strip-literal: specifier: ^3.0.0 version: 3.0.0 @@ -869,8 +869,8 @@ importers: playground/lib: devDependencies: sirv: - specifier: ^3.0.1 - version: 3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1) + specifier: ^3.0.2 + version: 3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95) playground/minify: dependencies: @@ -1419,8 +1419,8 @@ importers: specifier: ^5.1.0 version: 5.1.0 sirv: - specifier: ^3.0.1 - version: 3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1) + specifier: ^3.0.2 + version: 3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95) playground/ssr-conditions/external: {} @@ -6711,8 +6711,8 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - sirv@3.0.1: - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} sisteransi@1.0.5: @@ -12635,7 +12635,7 @@ snapshots: dependencies: is-arrayish: 0.3.2 - sirv@3.0.1(patch_hash=95b663b930c5cc6e609c7fa15b69a86ff3b30df68978611d1d3d724c65135cb1): + sirv@3.0.2(patch_hash=c07c56eb72faea34341d465cde2314e89db472106ed378181e3447893af6bf95): dependencies: '@polka/url': 1.0.0-next.28 mrmime: 2.0.1
pnpm-workspace.yaml+1 −1 modified@@ -15,7 +15,7 @@ overrides: vite: 'workspace:*' patchedDependencies: "http-proxy@1.18.1": "patches/http-proxy@1.18.1.patch" - "sirv@3.0.1": "patches/sirv@3.0.1.patch" + "sirv@3.0.2": "patches/sirv@3.0.2.patch" "chokidar@3.6.0": "patches/chokidar@3.6.0.patch" "dotenv-expand@12.0.2": "patches/dotenv-expand@12.0.2.patch" peerDependencyRules:
f0113f3f8266fix: do not serve file with dirname prefix
4 files changed · +37 −4
packages/sirv/index.mjs+6 −3 modified@@ -1,5 +1,5 @@ import * as fs from 'node:fs'; -import { join, normalize, resolve } from 'node:path'; +import { join, normalize, resolve, sep } from 'node:path'; import { totalist } from 'totalist/sync'; import { parse } from '@polka/url'; import { lookup } from 'mrmime'; @@ -39,7 +39,10 @@ function viaLocal(dir, isEtag, uri, extns) { let i=0, arr=toAssume(uri, extns); let abs, stats, name, headers; for (; i < arr.length; i++) { - abs = normalize(join(dir, name=arr[i])); + abs = normalize( + join(dir, name=arr[i]) + ); + if (abs.startsWith(dir) && fs.existsSync(abs)) { stats = fs.statSync(abs); if (stats.isDirectory()) continue; @@ -161,7 +164,7 @@ export default function (dir, opts={}) { }); } - let lookup = opts.dev ? viaLocal.bind(0, dir, isEtag) : viaCache.bind(0, FILES); + let lookup = opts.dev ? viaLocal.bind(0, dir + sep, isEtag) : viaCache.bind(0, FILES); return function (req, res, next) { let extns = [''];
tests/helpers.mjs+4 −1 modified@@ -33,7 +33,10 @@ export function http(opts) { send(method, path, opts) { let uri = new URL(path, address); return send(method, uri, opts); - } + }, + get address() { + return address; + }, }; }
tests/publicfile.txt+1 −0 added@@ -0,0 +1 @@ +hello
tests/sirv.mjs+26 −0 modified@@ -1,4 +1,5 @@ import { suite } from 'uvu'; +import * as http from 'node:http'; import * as assert from 'uvu/assert'; import sirv from '../packages/sirv/index.mjs'; import * as utils from './helpers.mjs'; @@ -304,6 +305,31 @@ security('should prevent directory traversal attacks :: dev', async () => { } }); +security('should prevent files starting with directory name :: dev', async () => { + let server = utils.http({ dev: true }); + let address = server.address; + + try { + let status = await new Promise((resolve, reject) => { + let req = http.request({ + hostname: address.hostname, + port: +address.port, + path: '/../publicfile.txt', + method: 'HEAD', + }, (res) => { + resolve(res.statusCode); + }); + + req.on('error', reject); + req.end(); + }); + + assert.is(status, 404); + } finally { + server.close(); + } +}); + security.run(); // ---
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-g4jq-h2w9-997cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-58751ghsaADVISORY
- github.com/lukeed/sirv/commit/f0113f3f8266328d804ee808f763a3c11f8997ebghsax_refsource_MISCWEB
- github.com/vitejs/vite/commit/09f2b52e8d5907f26602653caf41b3a56692600dghsax_refsource_MISCWEB
- github.com/vitejs/vite/commit/4f1c35bcbb5830290c694aa14b6789e07450f069ghsax_refsource_MISCWEB
- github.com/vitejs/vite/commit/63e2a5d232218f3f8d852056751e609a5367aaecghsax_refsource_MISCWEB
- github.com/vitejs/vite/commit/e11d24008b97d4ca731ecc1a3b95260a6d12e7e0ghsax_refsource_MISCWEB
- github.com/vitejs/vite/security/advisories/GHSA-g4jq-h2w9-997cghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.