CVE-2026-120338 breaks a boundary that AI application teams may assume is safe: Prefab UI’s Generative UI sandbox could let caller-supplied Python reach Deno’s host filesystem and network permissions. The issue affects prefab-ui 0.14.0 through 0.20.2 and is fixed in 0.21.0. Teams operating FastMCP or other services that enable Generative UI should inventory deployments, upgrade, review exposed hosts, and avoid treating WebAssembly alone as a security boundary.
Defender summary: The vulnerable feature is opt-in and requires Deno. Exposure is highest when an unauthenticated or broadly authorized client can call the Generative UI tool. Upgrade prefab-ui to 0.21.0 or later. Until then, remove the provider from reachable services rather than relying on the old sandbox.
What was disclosed
JFrog Security Research published CVE-2026-120338 on November 1, 2026. The project had already released prefab-ui 0.21.0 on October 26 with the relevant sandbox changes. The affected component runs generated Python inside Pyodide, a Python runtime compiled to WebAssembly, while a Deno subprocess provides the surrounding JavaScript environment.
The security failure was not a generic WebAssembly escape. The runner started Deno with filesystem read, filesystem write, and network permissions, then left the Deno API reachable through the JavaScript global object. Pyodide’s bridge to JavaScript made those host capabilities accessible to caller-supplied Python. In other words, the inner code did not need to break WebAssembly memory isolation; it could use permissions intentionally granted to the outer runtime.
Affected scope and exposure conditions
| Condition | Why it matters | Defender conclusion |
|---|---|---|
| prefab-ui 0.14.0 to 0.20.2 | Contains the vulnerable permission model | Upgrade is required |
| Generative UI provider enabled | Registers the code-generating execution path | Active exposure condition |
| Deno installed and available | The sandbox subprocess depends on Deno | Active exposure condition |
| Tool reachable without authentication | Any reachable client may be able to submit code | Highest-priority case |
| Tool requires authentication | Authorized tool callers may still cross the boundary | Patch; authentication is not a complete fix |
| prefab-ui 0.21.0 or later | Revokes Deno read, write, and network permissions before accepting generated code | Fixed version floor |
A vulnerable package in a lockfile is not automatically a remotely exploitable service. The Generative UI provider must be enabled, Deno must be available, and a caller must be able to reach the relevant tool. Those prerequisites are useful for prioritization, but they should not be used to postpone the upgrade. Configuration changes, new providers, or future deployments can activate dormant risk.
Why this matters for AI and MCP architectures
Generative interfaces turn model output into executable application behavior. That can be useful, but it also combines two difficult trust problems: an upstream model or client supplies code, and a runtime must enforce strict limits on what that code can see or change. The name “sandbox” is not proof that the boundary is complete.
- Capability inheritance: code inside a restricted runtime may still reach powerful APIs exposed by the host language bridge.
- Tool authorization: an authenticated MCP client can still be untrusted for a particular high-impact tool.
- Persistence opportunity: host filesystem write access can alter application environments or startup paths beyond the current request.
- Secret exposure: host files and environment-adjacent resources may contain tokens, configuration, or service credentials.
- Egress: network permission can turn local read access into data exfiltration if outbound traffic is unrestricted.
This is a concrete example of a broader AI-agent lesson: authorize generated actions according to their effects, not according to whether they arrive through a model, an MCP client, or a UI provider. The same control should apply to human-authored and model-authored code.
Immediate response plan
1. Find package and runtime exposure
Search Python lockfiles, container manifests, SBOMs, build logs, and deployed environments for prefab-ui. Record the exact installed version rather than the declared range alone. Then identify services that register FastMCP’s Generative UI provider or otherwise instantiate Prefab’s Pyodide sandbox. Confirm whether Deno is installed in the same image or host and whether the tool is reachable from untrusted networks or clients.
python3 -m pip show prefab-ui
python3 -m pip freeze | grep -i '^prefab-ui=='
command -v deno && deno --version
grep -R --line-number --exclude-dir=.git 'GenerativeUI' .Run these commands only in repositories and hosts you are authorized to inspect. A package manager may report a fixed library while a running container still uses an older image, so compare deployed image digests with the rebuilt artifact.
2. Contain high-risk services
If an affected service is reachable, remove the Generative UI provider or restrict the service to a trusted administration network until the upgrade is deployed. Do not assume authentication is sufficient: any principal allowed to call the vulnerable tool may be able to reach capabilities outside the intended sandbox. Restrict egress and protect application directories with least-privilege filesystem ownership as defense in depth.
3. Upgrade and rebuild
Upgrade to prefab-ui 0.21.0 or later, regenerate the lockfile, rebuild container images without cache where appropriate, and redeploy using a new immutable digest. Version 0.21.0 starts the Deno process with --no-prompt, limits bootstrap network access, and revokes filesystem read, filesystem write, and network permissions before generated code is accepted.
python3 -m pip install --upgrade 'prefab-ui>=0.21.0'
python3 -m pip show prefab-ui
python3 -m pip check4. Investigate before declaring closure
The public sources describe the vulnerability and a proof of concept, but they do not establish widespread exploitation. Do not label every affected host compromised. For exposed deployments, review MCP tool-call logs, authentication records, process creation, Deno activity, outbound connections, and changes to the application virtual environment. Compare files against a trusted build and rotate credentials if evidence shows that generated code could have read them.
Detection and forensic priorities
- Unexpected calls to the Generative UI tool, especially from new clients, anonymous sessions, or unusual source networks.
- Deno subprocesses launched outside normal deployment or request patterns.
- Writes under the Python virtual environment, particularly
site-packages, startup hooks, or dependency metadata after deployment. - Reads of configuration, credential, SSH, cloud, or service-account paths by the application identity.
- Outbound DNS and network connections from the sandbox process to destinations not required for approved bootstrap behavior.
- Drift between a running container filesystem and its signed or trusted image.
Preserve logs and a copy of the affected image before destructive cleanup when an incident is suspected. Local application logs may be incomplete, so correlate reverse-proxy, identity-provider, container-runtime, DNS, and network telemetry.
Hands-on lab: offline Prefab UI inventory triage
This lab classifies synthetic inventory. It never imports prefab-ui, starts Deno, exposes an MCP endpoint, or executes generated code. Use it to test triage logic before adapting the CSV fields to an approved SBOM or configuration export.
Prerequisites
- Python 3.9 or newer
- A disposable directory with no secrets
- No network access required
service,prefab_ui_version,generative_ui_enabled,deno_installed,network_exposed,owner
design-agent,0.20.2,yes,yes,yes,ai-platform
internal-prototype,0.18.0,no,yes,no,developer-tools
ui-gateway,0.21.0,yes,yes,yes,ai-platform
legacy-worker,0.13.2,yes,yes,no,data-platform
unknown-build,custom-main,yes,yes,yes,researchimport csv, re, sys
def parse_version(value):
match = re.fullmatch(r"(\d+)\.(\d+)\.(\d+)", value.strip())
return tuple(map(int, match.groups())) if match else None
def classify(row):
version = parse_version(row["prefab_ui_version"])
enabled = row["generative_ui_enabled"].lower() == "yes"
deno = row["deno_installed"].lower() == "yes"
exposed = row["network_exposed"].lower() == "yes"
if version is None:
return "REVIEW", "unrecognized version"
if version < (0, 14, 0):
return "OUT_OF_RANGE", "version predates affected range"
if version >= (0, 21, 0):
return "CURRENT", "fixed version or later"
if not enabled or not deno:
return "DORMANT", "affected package present; vulnerable path not active"
if exposed:
return "URGENT", "affected path active and network exposed"
return "PATCH", "affected path active"
with open(sys.argv[1], newline="", encoding="utf-8") as handle:
print("service,version,disposition,reason")
for row in csv.DictReader(handle):
disposition, reason = classify(row)
print(f'{row["service"]},{row["prefab_ui_version"]},{disposition},{reason}')Save the files as prefab_inventory.csv and audit_prefab_ui.py, then run:
python3 audit_prefab_ui.py prefab_inventory.csvExpected output:
service,version,disposition,reason
design-agent,0.20.2,URGENT,affected path active and network exposed
internal-prototype,0.18.0,DORMANT,affected package present; vulnerable path not active
ui-gateway,0.21.0,CURRENT,fixed version or later
legacy-worker,0.13.2,OUT_OF_RANGE,version predates affected range
unknown-build,custom-main,REVIEW,unrecognized versionTroubleshooting and cleanup
If Python cannot find the CSV, run pwd and ls -l to confirm both files are in the working directory. Keep REVIEW for development builds and nonstandard version strings; do not guess their security state. Delete the disposable directory after the exercise. The script was executed against the sample data, but it has not been tested against a production SBOM export.
Hardening beyond the patch
- Require authentication and explicit per-tool authorization for every MCP endpoint.
- Separate generated-code execution from the application control plane and sensitive secrets.
- Use a read-only root filesystem, minimal writable mounts, an unprivileged identity, and a restrictive seccomp or sandbox profile.
- Default-deny egress and permit only documented bootstrap destinations.
- Log tool identity, caller identity, request correlation, process creation, and policy denials.
- Test the negative security contract: generated code should fail when it requests host files, network access, environment secrets, or persistence.
For broader architecture controls, see Microsoft’s Five AI Agent Risk Classes. The filesystem-isolation lessons also connect directly to SharedRoot: The Real Risk of Shared Filesystem Access in Containerized AI Workloads.
Conclusion
CVE-2026-120338 is a reminder that an AI sandbox is only as strong as every capability exposed across its language and runtime bridges. Prefab UI 0.21.0 closes the documented Deno permission path, but operators should also narrow tool authorization, filesystem access, and network egress. Inventory first, prioritize reachable Generative UI services, upgrade through an immutable rebuild, and investigate exposed hosts before closing the issue.
Primary sources
- Prefab UI security advisory GHSA-595q-jv4r-q7j9.
- JFrog Security Research JFSA-2026-001699278, published November 1, 2026.
- Prefab UI 0.21.0 release, October 26, 2026.
- Prefab pull request 538.
- NVD CVE-2026-120338.
- CVE Program record.







