CVE-2022-35204
Description
Vitejs Vite before v2.9.13 was discovered to allow attackers to perform a directory traversal via a crafted URL to the victim's service.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Vite before v2.9.13 had a directory traversal bypass via URL-encoded path traversal sequences in the @fs endpoint.
Vulnerability
CVE-2022-35204 is a directory traversal vulnerability in Vite (Vitejs) versions prior to v2.9.13. The vulnerability arises from an incomplete fix for a previous path traversal issue (CVE-2022-2820). The server's @fs endpoint validates file access against an allow list, but the path validation was performed using decodeURI() instead of decodeURIComponent(). This discrepancy allowed attackers to bypass the allow list by URL-encoding path traversal sequences such as %2e%2e%2f (which decodes to ../), enabling access to files outside the intended serving directory [1][2][3].
Exploitation
An attacker could exploit this by crafting a URL with encoded traversal sequences targeted at the Vite dev server. For example, a request to http://localhost:3001/@fs/home/test/%2e%2e%2f%2e%2e%2fetc/passwd would bypass the allow list and access files outside the allowed scope [1]. The attack requires no authentication, as the Vite dev server typically listens on localhost. Tools like curl --path-as-is can be used to reproduce the issue, and the attack is effective across any Vite project running a vulnerable version [1].
Impact
Successful exploitation allows an attacker to read arbitrary files on the server's file system without authentication. This could lead to disclosure of sensitive data such as application source code, configuration files, or operating system files [1][2]. The vulnerability is classified with a medium severity (CVSS 5.3) due to the requirement that the attacker have network access to the dev server, which is often bound to localhost but can be exposed in some scenarios [2].
Mitigation
The vulnerability was addressed in Vite v2.9.13 by updating the URL decoding logic from decodeURI() to decodeURIComponent() in the serveStaticMiddleware and serveRawFsMiddleware [3][4]. A backported fix was provided for the v2.9.x release line [4]. Users are strongly advised to upgrade to v2.9.13 or later. No workarounds are documented, but restricting network exposure of the Vite dev server can reduce risk.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
vitenpm | < 2.9.13 | 2.9.13 |
vitenpm | >= 3.0.0-alpha.0, < 3.0.0-beta.4 | 3.0.0-beta.4 |
Affected products
2- Vitejs/Vitedescription
Patches
2e109d64331d9fix: backport #8804, /@fs/ dir traversal with escaped chars (fixes #8498) (#8805)
3 files changed · +41 −2
packages/playground/fs-serve/root/src/index.html+27 −0 modified@@ -17,6 +17,8 @@ <h2>Safe Fetch Subdirectory</h2> <h2>Unsafe Fetch</h2> <pre class="unsafe-fetch-status"></pre> <pre class="unsafe-fetch"></pre> +<pre class="unsafe-fetch-8498-status"></pre> +<pre class="unsafe-fetch-8498"></pre> <h2>Safe /@fs/ Fetch</h2> <pre class="safe-fs-fetch-status"></pre> @@ -27,6 +29,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-8498-status"></pre> +<pre class="unsafe-fs-fetch-8498"></pre> <h2>Nested Entry</h2> <pre class="nested-entry"></pre> @@ -83,6 +87,19 @@ <h2>Denied</h2> console.error(e) }) + // outside of allowed dir with special characters #8498 + fetch('/src/%2e%2e%2funsafe%2etxt') + .then((r) => { + text('.unsafe-fetch-8498-status', r.status) + return r.text() + }) + .then((data) => { + text('.unsafe-fetch-8498', data) + }) + .catch((e) => { + console.error(e) + }) + // imported before, should be treated as safe fetch('/@fs/' + ROOT + '/safe.json') .then((r) => { @@ -106,6 +123,16 @@ <h2>Denied</h2> console.error(e) }) + // outside root with special characters #8498 + fetch('/@fs/' + ROOT + '/root/src/%2e%2e%2f%2e%2e%2funsafe%2ejson') + .then((r) => { + text('.unsafe-fs-fetch-8498-status', r.status) + return r.json() + }) + .then((data) => { + text('.unsafe-fs-fetch-8498', JSON.stringify(data)) + }) + // not imported before, inside root with special characters, treated as safe fetch( '/@fs/' +
packages/playground/fs-serve/__tests__/fs-serve.spec.ts+12 −0 modified@@ -37,6 +37,13 @@ describe('main', () => { expect(await page.textContent('.unsafe-fetch-status')).toBe('403') }) + test('unsafe fetch with special characters (#8498)', async () => { + expect(await page.textContent('.unsafe-fetch-8498')).toMatch( + '403 Restricted' + ) + expect(await page.textContent('.unsafe-fetch-8498-status')).toBe('403') + }) + test('safe fs fetch', async () => { expect(await page.textContent('.safe-fs-fetch')).toBe(stringified) expect(await page.textContent('.safe-fs-fetch-status')).toBe('200') @@ -54,6 +61,11 @@ describe('main', () => { expect(await page.textContent('.unsafe-fs-fetch-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') + }) + test('nested entry', async () => { expect(await page.textContent('.nested-entry')).toBe('foobar') })
packages/vite/src/node/server/middlewares/static.ts+2 −2 modified@@ -78,7 +78,7 @@ export function serveStaticMiddleware( return next() } - const url = decodeURI(req.url!) + const url = decodeURIComponent(req.url!) // apply aliases to static requests as well let redirected: string | undefined @@ -121,7 +121,7 @@ export function serveRawFsMiddleware( // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` return function viteServeRawFsMiddleware(req, res, next) { - let url = decodeURI(req.url!) + let url = decodeURIComponent(req.url!) // In some cases (e.g. linked monorepos) files outside of root will // reference assets that are also out of served root. In such cases // the paths are rewritten to `/@fs/` prefixed paths and must be served by
6851009e6725fix: /@fs/ dir traversal with escaped chars (fixes #8498) (#8804)
3 files changed · +41 −2
packages/vite/src/node/server/middlewares/static.ts+2 −2 modified@@ -80,7 +80,7 @@ export function serveStaticMiddleware( return next() } - const url = decodeURI(req.url!) + const url = decodeURIComponent(req.url!) // apply aliases to static requests as well let redirected: string | undefined @@ -123,7 +123,7 @@ export function serveRawFsMiddleware( // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` return function viteServeRawFsMiddleware(req, res, next) { - let url = decodeURI(req.url!) + let url = decodeURIComponent(req.url!) // In some cases (e.g. linked monorepos) files outside of root will // reference assets that are also out of served root. In such cases // the paths are rewritten to `/@fs/` prefixed paths and must be served by
playground/fs-serve/root/src/index.html+27 −0 modified@@ -17,6 +17,8 @@ <h2>Safe Fetch Subdirectory</h2> <h2>Unsafe Fetch</h2> <pre class="unsafe-fetch-status"></pre> <pre class="unsafe-fetch"></pre> +<pre class="unsafe-fetch-8498-status"></pre> +<pre class="unsafe-fetch-8498"></pre> <h2>Safe /@fs/ Fetch</h2> <pre class="safe-fs-fetch-status"></pre> @@ -27,6 +29,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-8498-status"></pre> +<pre class="unsafe-fs-fetch-8498"></pre> <h2>Nested Entry</h2> <pre class="nested-entry"></pre> @@ -83,6 +87,19 @@ <h2>Denied</h2> console.error(e) }) + // outside of allowed dir with special characters #8498 + fetch('/src/%2e%2e%2funsafe%2etxt') + .then((r) => { + text('.unsafe-fetch-8498-status', r.status) + return r.text() + }) + .then((data) => { + text('.unsafe-fetch-8498', data) + }) + .catch((e) => { + console.error(e) + }) + // imported before, should be treated as safe fetch('/@fs/' + ROOT + '/safe.json') .then((r) => { @@ -106,6 +123,16 @@ <h2>Denied</h2> console.error(e) }) + // outside root with special characters #8498 + fetch('/@fs/' + ROOT + '/root/src/%2e%2e%2f%2e%2e%2funsafe%2ejson') + .then((r) => { + text('.unsafe-fs-fetch-8498-status', r.status) + return r.json() + }) + .then((data) => { + text('.unsafe-fs-fetch-8498', JSON.stringify(data)) + }) + // not imported before, inside root with special characters, treated as safe fetch( '/@fs/' +
playground/fs-serve/__tests__/fs-serve.spec.ts+12 −0 modified@@ -35,6 +35,13 @@ describe.runIf(isServe)('main', () => { expect(await page.textContent('.unsafe-fetch-status')).toBe('403') }) + test('unsafe fetch with special characters (#8498)', async () => { + expect(await page.textContent('.unsafe-fetch-8498')).toMatch( + '403 Restricted' + ) + expect(await page.textContent('.unsafe-fetch-8498-status')).toBe('403') + }) + test('safe fs fetch', async () => { expect(await page.textContent('.safe-fs-fetch')).toBe(stringified) expect(await page.textContent('.safe-fs-fetch-status')).toBe('200') @@ -52,6 +59,11 @@ describe.runIf(isServe)('main', () => { expect(await page.textContent('.unsafe-fs-fetch-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') + }) + test('nested entry', async () => { expect(await page.textContent('.nested-entry')).toBe('foobar') })
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-mv48-hcvh-8jj8ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-35204ghsaADVISORY
- github.com/vitejs/vite/commit/6851009e6725b17608113a5a63474280075cae1cghsaWEB
- github.com/vitejs/vite/commit/e109d64331d9fa57753832762c3573c3532a6947ghsaWEB
- github.com/vitejs/vite/issues/8498ghsax_refsource_MISCWEB
- github.com/vitejs/vite/releases/tag/v2.9.13ghsax_refsource_MISCWEB
- github.com/vitejs/vite/releases/tag/v3.0.0-beta.4ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.