| | import type { McpUiSandboxProxyReadyNotification, McpUiSandboxResourceReadyNotification } from "../../../dist/src/types"; |
| | import { buildAllowAttribute } from "../../../dist/src/app-bridge"; |
| |
|
| | const ALLOWED_REFERRER_PATTERN = /^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/; |
| |
|
| | if (window.self === window.top) { |
| | throw new Error("This file is only to be used in an iframe sandbox."); |
| | } |
| |
|
| | if (!document.referrer) { |
| | throw new Error("No referrer, cannot validate embedding site."); |
| | } |
| |
|
| | if (!document.referrer.match(ALLOWED_REFERRER_PATTERN)) { |
| | throw new Error( |
| | `Embedding domain not allowed in referrer ${document.referrer}. (Consider updating the validation logic to allow your domain.)`, |
| | ); |
| | } |
| |
|
| | |
| | |
| | const EXPECTED_HOST_ORIGIN = new URL(document.referrer).origin; |
| |
|
| | const OWN_ORIGIN = new URL(window.location.href).origin; |
| |
|
| | |
| | |
| | |
| | try { |
| | window.top!.alert("If you see this, the sandbox is not setup securely."); |
| | throw "FAIL"; |
| | } catch (e) { |
| | if (e === "FAIL") { |
| | throw new Error("The sandbox is not setup securely."); |
| | } |
| |
|
| | |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | const inner = document.createElement("iframe"); |
| | inner.style = "width:100%; height:100%; border:none;"; |
| | inner.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms"); |
| | |
| | |
| | document.body.appendChild(inner); |
| |
|
| | const RESOURCE_READY_NOTIFICATION: McpUiSandboxResourceReadyNotification["method"] = |
| | "ui/notifications/sandbox-resource-ready"; |
| | const PROXY_READY_NOTIFICATION: McpUiSandboxProxyReadyNotification["method"] = |
| | "ui/notifications/sandbox-proxy-ready"; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | window.addEventListener("message", async (event) => { |
| | if (event.source === window.parent) { |
| | |
| | |
| | if (event.origin !== EXPECTED_HOST_ORIGIN) { |
| | console.error( |
| | "[Sandbox] Rejecting message from unexpected origin:", |
| | event.origin, |
| | "expected:", |
| | EXPECTED_HOST_ORIGIN |
| | ); |
| | return; |
| | } |
| |
|
| | if (event.data && event.data.method === RESOURCE_READY_NOTIFICATION) { |
| | const { html, sandbox, permissions } = event.data.params; |
| | if (typeof sandbox === "string") { |
| | inner.setAttribute("sandbox", sandbox); |
| | } |
| | |
| | const allowAttribute = buildAllowAttribute(permissions); |
| | if (allowAttribute) { |
| | console.log("[Sandbox] Setting allow attribute:", allowAttribute); |
| | inner.setAttribute("allow", allowAttribute); |
| | } |
| | if (typeof html === "string") { |
| | |
| | const doc = inner.contentDocument || inner.contentWindow?.document; |
| | if (doc) { |
| | doc.open(); |
| | doc.write(html); |
| | doc.close(); |
| | } else { |
| | |
| | console.warn("[Sandbox] document.write not available, falling back to srcdoc"); |
| | inner.srcdoc = html; |
| | } |
| | } |
| | } else { |
| | if (inner && inner.contentWindow) { |
| | inner.contentWindow.postMessage(event.data, "*"); |
| | } |
| | } |
| | } else if (event.source === inner.contentWindow) { |
| | if (event.origin !== OWN_ORIGIN) { |
| | console.error( |
| | "[Sandbox] Rejecting message from inner iframe with unexpected origin:", |
| | event.origin, |
| | "expected:", |
| | OWN_ORIGIN |
| | ); |
| | return; |
| | } |
| | |
| | |
| | window.parent.postMessage(event.data, EXPECTED_HOST_ORIGIN); |
| | } |
| | }); |
| |
|
| | |
| | |
| | window.parent.postMessage({ |
| | jsonrpc: "2.0", |
| | method: PROXY_READY_NOTIFICATION, |
| | params: {}, |
| | }, EXPECTED_HOST_ORIGIN); |
| |
|