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
This commit is contained in:
parent
ca09c415bf
commit
b4c6019d47
3 changed files with 116 additions and 66 deletions
|
|
@ -8,7 +8,7 @@ import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|||
import { AppRenderer, AppRendererProps } from "../src/AppRenderer";
|
||||
import { AppBridge } from "../../../dist/src/app-bridge";
|
||||
|
||||
const SANDBOX_PROXY_URL = URL.parse("/sandbox.html", location.href)!;
|
||||
const SANDBOX_PROXY_URL = new URL("http://localhost:8081/sandbox.html");
|
||||
|
||||
/**
|
||||
* Example React application demonstrating the AppRenderer component.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import {
|
|||
McpUiSizeChangeNotificationSchema,
|
||||
} from "@modelcontextprotocol/ext-apps";
|
||||
|
||||
const SANDBOX_PROXY_URL = URL.parse("/sandbox.html", location.href)!;
|
||||
const SANDBOX_PROXY_URL = new URL("http://localhost:8081/sandbox.html");
|
||||
|
||||
window.addEventListener("load", async () => {
|
||||
const client = new Client({
|
||||
|
|
|
|||
|
|
@ -1,16 +1,43 @@
|
|||
// Double-iframe raw HTML mode (HTML sent via postMessage)
|
||||
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;";
|
||||
// sandbox will be set from postMessage payload; default minimal before html arrives
|
||||
inner.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
|
||||
document.body.appendChild(inner);
|
||||
|
||||
// Wait for HTML content from parent
|
||||
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"
|
||||
event.data.method ===
|
||||
("ui/notifications/sandbox-resource-ready" as McpUiSandboxResourceReadyNotification["method"])
|
||||
) {
|
||||
const { html, sandbox } = event.data.params;
|
||||
if (typeof sandbox === "string") {
|
||||
|
|
@ -34,7 +61,8 @@ window.addEventListener("message", async (event) => {
|
|||
window.parent.postMessage(
|
||||
{
|
||||
jsonrpc: "2.0",
|
||||
method: "ui/notifications/sandbox-proxy-ready",
|
||||
method:
|
||||
"ui/notifications/sandbox-proxy-ready" as McpUiSandboxProxyReadyNotification["method"],
|
||||
params: {},
|
||||
},
|
||||
"*",
|
||||
|
|
|
|||
Loading…
Reference in a new issue