Download examples/basic-host/src/sandbox.ts from AbdulElahGwaith/ext-appss: direct link, hf CLI and curl.
- Browser
- Download file 5.47 kB
-
https://huggingface.co/AbdulElahGwaith/ext-appss/resolve/main/examples/basic-host/src/sandbox.ts
- Command line
-
hf download hf://AbdulElahGwaith/ext-appss/examples/basic-host/src/sandbox.ts
-
curl -L -o sandbox.ts https://huggingface.co/AbdulElahGwaith/ext-appss/resolve/main/examples/basic-host/src/sandbox.ts
5.47 kB
| 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.)`, | |
| ); | |
| } | |
| // Extract the expected host origin from the referrer for origin validation. | |
| // This is the origin we expect all parent messages to come from. | |
| const EXPECTED_HOST_ORIGIN = new URL(document.referrer).origin; | |
| const OWN_ORIGIN = new URL(window.location.href).origin; | |
| // Security self-test: verify iframe isolation is working correctly. | |
| // This MUST throw a SecurityError -- if `window.top` is accessible, the sandbox | |
| // configuration is dangerously broken and untrusted content could escape. | |
| 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."); | |
| } | |
| // Expected: SecurityError confirms proper sandboxing. | |
| } | |
| // Double-iframe sandbox architecture: THIS file is the outer sandbox proxy | |
| // iframe on a separate origin. It creates an inner iframe for untrusted HTML | |
| // content. Per the specification, the Host and the Sandbox MUST have different | |
| // origins. | |
| const inner = document.createElement("iframe"); | |
| inner.style = "width:100%; height:100%; border:none;"; | |
| inner.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms"); | |
| // Note: allow attribute is set later when receiving sandbox-resource-ready notification | |
| // based on the permissions requested by the app | |
| 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"; | |
| // Message relay: This Sandbox (outer iframe) acts as a bidirectional bridge, | |
| // forwarding messages between: | |
| // | |
| // Host (parent window) ↔ Sandbox (outer frame) ↔ Guest UI (inner iframe) | |
| // | |
| // Reason: the parent window and inner iframe have different origins and can't | |
| // communicate directly, so the outer iframe forwards messages in both | |
| // directions to connect them. | |
| // | |
| // Special case: The "ui/notifications/sandbox-proxy-ready" message is | |
| // intercepted here (not relayed) because the Sandbox uses it to configure and | |
| // load the inner iframe with the Guest UI HTML content. | |
| // | |
| // Security: CSP is enforced via HTTP headers on sandbox.html (set by serve.ts | |
| // based on ?csp= query param). This is tamper-proof unlike meta tags. | |
| window.addEventListener("message", async (event) => { | |
| if (event.source === window.parent) { | |
| // Validate that messages from parent come from the expected host origin. | |
| // This prevents malicious pages from sending messages to this sandbox. | |
| 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); | |
| } | |
| // Set Permission Policy allow attribute if permissions are requested | |
| const allowAttribute = buildAllowAttribute(permissions); | |
| if (allowAttribute) { | |
| console.log("[Sandbox] Setting allow attribute:", allowAttribute); | |
| inner.setAttribute("allow", allowAttribute); | |
| } | |
| if (typeof html === "string") { | |
| // Use document.write instead of srcdoc (which the CesiumJS Map won't work with) | |
| const doc = inner.contentDocument || inner.contentWindow?.document; | |
| if (doc) { | |
| doc.open(); | |
| doc.write(html); | |
| doc.close(); | |
| } else { | |
| // Fallback to srcdoc if document is not accessible | |
| 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; | |
| } | |
| // Relay messages from inner frame to parent window. | |
| // Use specific origin instead of "*" to prevent message interception. | |
| window.parent.postMessage(event.data, EXPECTED_HOST_ORIGIN); | |
| } | |
| }); | |
| // Notify the Host that the Sandbox is ready to receive Guest UI HTML. | |
| // Use specific origin instead of "*" to ensure only the expected host receives this. | |
| window.parent.postMessage({ | |
| jsonrpc: "2.0", | |
| method: PROXY_READY_NOTIFICATION, | |
| params: {}, | |
| }, EXPECTED_HOST_ORIGIN); | |