From ac2812ba2f0edd5d8a4bdee8871fe1791ab32db4 Mon Sep 17 00:00:00 2001 From: Dustin Healy <54083382+dustinhealy@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:47:28 -0700 Subject: [PATCH] fix(mcp): use window.location.origin as trusted sandbox origin The previous approach derived trustedOrigin from document.referrer at startup and fell back to '*' when referrer was empty, with a lazy-set from the first incoming message as a further fallback. Both paths leave a window where notifyReady broadcasts to all frames or the origin can be set by an untrusted first message. The sandbox is always served same-origin with LibreChat (/api/mcp/sandbox), so window.location.origin is always the exact parent origin. This replaces the referrer parse and lazy-set entirely: trustedOrigin is a const set at parse time, notifyReady uses it directly, and the message handler rejects any message whose origin does not match without fallback. --- client/public/mcp-sandbox.html | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/client/public/mcp-sandbox.html b/client/public/mcp-sandbox.html index 2326af4cae..db62a6d8ee 100644 --- a/client/public/mcp-sandbox.html +++ b/client/public/mcp-sandbox.html @@ -18,26 +18,21 @@ let readyInterval = null; const SANDBOX_PREFIX = 'ui/notifications/sandbox-'; - // Derive the trusted origin from the referrer at startup. - // The sandbox is always served same-origin, so document.referrer is the LibreChat page URL. - let trustedOrigin = null; - try { - if (document.referrer) { - trustedOrigin = new URL(document.referrer).origin; - } - } catch {} + // The sandbox is always served same-origin with LibreChat, so window.location.origin + // is the exact expected parent origin. No referrer fallback or lazy-set needed. + const trustedOrigin = window.location.origin; function notifyReady() { window.parent.postMessage( { jsonrpc: '2.0', method: 'ui/notifications/sandbox-proxy-ready', params: {} }, - trustedOrigin || '*' + trustedOrigin ); if (!readyInterval) { readyInterval = setInterval(() => { if (!innerFrame) { window.parent.postMessage( { jsonrpc: '2.0', method: 'ui/notifications/sandbox-proxy-ready', params: {} }, - trustedOrigin || '*' + trustedOrigin ); } }, 500); @@ -54,9 +49,7 @@ if (!msg || msg.jsonrpc !== '2.0') return; if (event.source === window.parent) { - if (!trustedOrigin) { - trustedOrigin = event.origin; - } else if (event.origin !== trustedOrigin) { + if (event.origin !== trustedOrigin) { return; } @@ -76,9 +69,7 @@ if (msg.method && msg.method.startsWith(SANDBOX_PREFIX)) { return; } - if (trustedOrigin) { - window.parent.postMessage(msg, trustedOrigin); - } + window.parent.postMessage(msg, trustedOrigin); } });