ext-apps/examples/simple-host/src/sandbox.ts
Olivier Chafik b4c6019d47
example-host: fix sandboxing setup (#21)
* example-host: prevent accidental non-iframing of sandbox (or iframing in prod)

* fix sandbox isolation + "test in prod"

* Update package.json

* Update sandbox.ts
2025-11-25 02:45:55 +00:00

69 lines
2.1 KiB
TypeScript

import type {
McpUiSandboxProxyReadyNotification,
McpUiSandboxResourceReadyNotification,
} from "../../../dist/src/types";
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(/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/)) {
throw new Error(
`Embedding domain not allowed in referrer ${document.referrer} (update the validation logic to allow your domain)`,
);
}
// Try and break out of this iframe
try {
window.top!.alert("If you see this, the sandbox is not setup securely.");
throw new Error(
"Managed to break out of iframe, the sandbox is not setup securely.",
);
} catch (e) {
// Ignore
}
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);
window.addEventListener("message", async (event) => {
// Note: in production you'll also want to validate event.origin against your outer domain.
if (event.source === window.parent) {
if (
event.data &&
event.data.method ===
("ui/notifications/sandbox-resource-ready" as McpUiSandboxResourceReadyNotification["method"])
) {
const { html, sandbox } = event.data.params;
if (typeof sandbox === "string") {
inner.setAttribute("sandbox", sandbox);
}
if (typeof html === "string") {
inner.srcdoc = html;
}
} else {
if (inner && inner.contentWindow) {
inner.contentWindow.postMessage(event.data, "*");
}
}
} else if (event.source === inner.contentWindow) {
// Relay messages from inner to parent
window.parent.postMessage(event.data, "*");
}
});
// Notify parent that proxy is ready to receive HTML (distinct event)
window.parent.postMessage(
{
jsonrpc: "2.0",
method:
"ui/notifications/sandbox-proxy-ready" as McpUiSandboxProxyReadyNotification["method"],
params: {},
},
"*",
);