A locked isolation room where the guard forgot to remove the master key
This is a sandbox escape in Prefab UI's Generative UI feature. The sandbox is supposed to isolate untrusted Python code, including LLM-written UI, inside Pyodide in a Deno subprocess. But in prefab-ui 0.14.0–0.20.2, the Deno launcher starts with --allow-read, --allow-write, and --allow-net, and the runner leaves the Deno API on the JavaScript global object. Pyodide's js module proxies that global, so Python code can call Deno.readTextFileSync and Deno.writeTextFileSync directly. The result is arbitrary file read/write as the server OS user, plus persistence by writing a .pth file into the server virtualenv's site-packages.
The vendor rates this CRITICAL 10, and the impact is genuinely severe when the path is reachable. But the reachable population is much smaller than the score suggests. Generative UI is opt-in, the host must have Deno installed, and the remote path requires a FastMCP server exposing the generate_prefab_ui tool with no authentication or an attacker who can call tools. FastMCP's default transport is stdio, not a network socket. So the vulnerability is a reliable RCE when the conditions align, but not a default-everywhere emergency.
4 steps from start to impact.
Reach an unauthenticated FastMCP GenerativeUI endpoint
transport="http" and no auth middleware, the MCP handshake and tool list are open. An attacker can use an MCP client or raw JSON-RPC 2.0 to list tools; generate_prefab_ui appears if GenerativeUI is enabled.- FastMCP server bound to a network-accessible interface
- No authentication configured, or attacker has valid tool-calling credentials
- GenerativeUI provider registered and
generate_prefab_uiexposed
- FastMCP default transport is stdio, not network
- Most early MCP deployments are local to a single AI client
- Authentication or an API gateway commonly fronts internet-exposed MCP servers
Submit Python payload through the MCP tool
generate_prefab_ui tool accepts a code argument and forwards it into the Prefab sandbox without sanitization. Because the MCP tool schema is public, the attacker crafts a JSON-RPC tools/call with malicious Python. The code does not need to be valid UI; any Python is accepted.- MCP tool call permitted
- Payload satisfies the tool input schema; no code sanitization
- GenerativeUI feature flag set on the server
- Some servers require an OAuth bearer token if auth is enabled
- The tool may be hidden unless the GenerativeUI feature is explicitly enabled
generate_prefab_ui calls; no standard IDS signature exists.Escape the JS sandbox via Pyodide's js proxy
--allow-read, --allow-write, and --allow-net, and the runner leaves the Deno object on globalThis. Pyodide's js module wraps globalThis, so Python code can use from js import Deno and call Deno.readTextFileSync, Deno.writeTextFileSync, or fetch arbitrary URLs. No browser same-origin restriction applies because this is a host Deno process.- Deno installed and executable by the FastMCP server process
prefab-ui0.14.0–0.20.2 running the vulnerable launcher
- Deno is not installed by default on most Python server hosts
- Generative UI is opt-in, so many installs never start the subprocess
Read secrets and plant persistence in site-packages
.env, cloud credentials, or source. It can write a .pth file into the active virtualenv's site-packages. On the next Python startup, that file executes arbitrary code, giving a persistent shell even after the tool call ends.- Server OS user has write access to virtualenv
site-packages - Attacker chooses a payload compatible with Python startup
- Containers with read-only filesystems or non-writable
site-packagesblock persistence - Least-privilege service accounts reduce lateral movement after initial compromise
site-packages can flag new .pth files; EDR can alert on Deno.writeTextFileSync to sensitive paths.prefab-ui to 0.21.0 immediately if Generative UI is enabled — The fix revokes Deno read/write/network permissions after runtime load, starts Deno with --no-prompt, and limits bootstrap network to cdn.jsdelivr.net. Apply this as the primary remediation within the noisgate mitigation SLA for HIGH: ≤30 days, and complete remediation within ≤180 days.generate_prefab_ui tool until patched — Removing the tool eliminates the attack surface entirely, even on vulnerable versions. If you cannot patch within the mitigation window, turn the feature off in the server configuration.site-packages — Limits the attacker's ability to read sensitive files and blocks .pth persistence. This does not stop code execution inside the process, but it reduces the post-exploitation blast radius.site-packages — Use EDR or FIM to alert on new .pth files and on Deno processes spawned by Python services. Detection is a compensating control only; it does not prevent the exploit.- Running the MCP server in a Docker container alone does not stop the exploit: the Deno subprocess runs with the container's file view and can read/write any mounted secret or bind-mounted
site-packages. - WAF or API gateway allow-listing MCP endpoints without auth does not help if the tool remains unauthenticated at the application layer.
- Network egress filtering to cdn.jsdelivr.net does not stop file read/write because
Deno.readTextFileSyncandDeno.writeTextFileSyncare local syscalls.
The supporting signals.
| In-the-wild status | No known in-the-wild exploitation as of the 2026-11-01 disclosure; not currently tied to any tracked campaign. |
|---|---|
| Proof of concept | No public PoC repo named after CVE-2026-120338 identified in provided intelligence. The exploit is straightforward once the tool is reachable: from js import Deno and call Deno.writeTextFileSync. |
| EPSS | No EPSS score published for this CVE yet; given the opt-in niche component, the initial exploitation percentile is likely low. |
| KEV | Not listed in CISA KEV as of 2026-11-01. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H — assumes unauthenticated network access with no preconditions and a scope change from sandbox to host. |
| Affected versions | prefab-ui 0.14.0 through 0.20.2. |
| Fixed versions | prefab-ui 0.21.0; commit 030f074 / pull request 538. |
| Exposure data | No reliable Shodan/Censys/GreyNoise profile for generate_prefab_ui endpoints. FastMCP HTTP deployments are not yet broadly fingerprinted. |
| Disclosure date | 2026-11-01. |
| CWE | CWE-94: Code Injection |
Sources.
Why this verdict
- Unrestricted Deno flags make the escape trivial once reached: The launcher grants
--allow-read,--allow-write, and--allow-net, and the runner leavesDenoon the JS global, so the sandbox provides no real isolation. - But the reachable population shrinks sharply: Generative UI is opt-in, Deno must be installed, and FastMCP must be exposed as an HTTP/SSE server without authentication. FastMCP's default transport is stdio, so most deployments are not reachable over the network.
- No observed exploitation or KEV listing: Absent in-the-wild activity, a CRITICAL 10 overstates real-world urgency for most teams.
- Role multiplier: The affected component is an opt-in generative UI sandbox in a Python MCP server library. Typical installs are developer workstations or single-purpose app tiers, not canonical high-value roles such as IdP, hypervisor, backup, or CI/CD. Worst-case blast radius is host-level file read/write and persistence, not domain/fleet take-over. That caps the floor below CRITICAL but still supports HIGH when unauthenticated network exposure is present.
Why not higher?
Not CRITICAL because the vulnerable path requires three opt-in/deployment choices that most FastMCP servers do not make: generative UI enabled, Deno installed, and the server bound to a reachable interface without authentication. The component is not a canonical high-value role, so network-adjacent RCE does not automatically compound to fleet or domain compromise.
Why not lower?
Not MEDIUM because when the conditions are met, exploitation is unauthenticated, low complexity, and yields arbitrary file read/write plus code persistence via .pth. A public-facing FastMCP server with GenerativeUI enabled and Deno present gives an attacker a reliable host-level foothold.
Crowdsourced verification payload.
Run this on the host running the FastMCP server, as the same service account (or root if needed to inspect the virtualenv). Invoke as python3 check_cve_2026_120338.py. It checks the installed prefab-ui version and whether Deno is available on PATH.
#!/usr/bin/env python3
"""Check for CVE-2026-120338 in prefab-ui / FastMCP Generative UI deployments."""
import importlib.metadata
import shutil
import sys
def parse_version(v):
parts = []
for x in v.split("."):
try:
parts.append(int(x.split("-")[0].split("+")[0]))
except ValueError:
parts.append(0)
while len(parts) < 3:
parts.append(0)
return tuple(parts[:3])
try:
ver = importlib.metadata.version("prefab-ui")
except importlib.metadata.PackageNotFoundError:
print("PATCHED: prefab-ui is not installed")
sys.exit(0)
v = parse_version(ver)
min_vulnerable = (0, 14, 0)
max_vulnerable = (0, 20, 2)
if min_vulnerable <= v <= max_vulnerable:
deno = shutil.which("deno")
if deno:
print(f"VULNERABLE: prefab-ui {ver} and Deno installed at {deno}")
sys.exit(1)
else:
print(f"UNKNOWN: prefab-ui {ver} is vulnerable but Deno is not installed on PATH")
sys.exit(2)
else:
print(f"PATCHED: prefab-ui {ver} is outside vulnerable range")
sys.exit(0)