Skip to content

Commit a6109b0

Browse files
authored
fix: resolve sandbox origin and Vite module pathing issues for sandboxed iframes (a2ui-project#1318)
- add missing node_modules dependencies for basic server app
1 parent 55126d5 commit a6109b0

2 files changed

Lines changed: 26 additions & 16 deletions

File tree

samples/client/lit/custom-components-example/vite.config.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ export default async () => {
3939
configureServer(server) {
4040
server.middlewares.use((req, _res, next) => {
4141
if (req.url?.startsWith(`/${SANDBOX_BASE_PATH}`)) {
42-
req.url = '/@fs' + resolve(__dirname, '../../' + req.url.slice(1));
42+
let targetPath = req.url.slice(1);
43+
// Normalize .js requests from HTML back to source .ts files for Vite bundling
44+
if (targetPath.endsWith(".js")) {
45+
targetPath = targetPath.slice(0, -3) + ".ts";
46+
}
47+
req.url = '/@fs' + resolve(__dirname, '../../' + targetPath);
4348
}
4449
next();
4550
});
@@ -68,6 +73,12 @@ export default async () => {
6873
},
6974
server: {
7075
host: true, // Listen on all network interfaces (0.0.0.0), enabling both localhost and 127.0.0.1 simultaneously
76+
fs: {
77+
allow: [
78+
"../../",
79+
"./",
80+
]
81+
}
7182
},
7283
} satisfies UserConfig;
7384
};

samples/client/shared/mcp_apps_inner_iframe/sandbox.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ const PROXY_READY_NOTIFICATION: McpUiSandboxProxyReadyNotification["method"] =
7373

7474
window.addEventListener("message", async (event) => {
7575
if (event.source === window.parent) {
76-
if (event.origin !== EXPECTED_HOST_ORIGIN) {
76+
const normalizeOrigin = (origin: string) => origin.replace("://127.0.0.1", "://localhost");
77+
if (normalizeOrigin(event.origin) !== normalizeOrigin(EXPECTED_HOST_ORIGIN)) {
7778
console.error(
7879
"[Sandbox] Rejecting message from unexpected origin:",
7980
event.origin,
@@ -95,34 +96,32 @@ window.addEventListener("message", async (event) => {
9596
if (typeof html === "string") {
9697
const sendInit = () => {
9798
if (inner.contentWindow) {
98-
inner.contentWindow.postMessage({ type: "sandbox-init" }, OWN_ORIGIN);
99+
// Use "*" because sandboxed iframes with no 'allow-same-origin' have a "null" origin.
100+
// A specific target origin will fail matching and block delivery.
101+
inner.contentWindow.postMessage({ type: "sandbox-init" }, "*");
99102
}
100103
};
101104
inner.onload = sendInit;
102105

103-
const doc = inner.contentDocument || inner.contentWindow?.document;
104-
if (doc) {
105-
doc.open();
106-
doc.write(html);
107-
doc.close();
108-
// doc.write doesn't always trigger iframe onload reliably.
109-
Promise.resolve().then(sendInit);
110-
} else {
111-
inner.srcdoc = html;
112-
}
106+
inner.srcdoc = html;
113107
}
114108
} else {
115109
if (inner && inner.contentWindow) {
116-
inner.contentWindow.postMessage(event.data, OWN_ORIGIN);
110+
// Same rationale as above: target origin must be "*" to reach sandboxed 'null' windows.
111+
inner.contentWindow.postMessage(event.data, "*");
117112
}
118113
}
119114
} else if (event.source === inner.contentWindow) {
120-
if (event.origin !== OWN_ORIGIN) {
115+
// MUST allow the string "null" as the origin. Without 'allow-same-origin',
116+
// browsers force sandboxed frames to an anonymous, unique origin that serializes to "null".
117+
// Security verification relies primarily on ensuring 'event.source === inner.contentWindow'.
118+
if (event.origin !== OWN_ORIGIN && event.origin !== "null") {
121119
console.error(
122120
"[Sandbox] Rejecting message from inner iframe with unexpected origin:",
123121
event.origin,
124122
"expected:",
125-
OWN_ORIGIN
123+
OWN_ORIGIN,
124+
"or null"
126125
);
127126
return;
128127
}

0 commit comments

Comments
 (0)