From f57cf4c8949dac5c0d6a5ec024a5578f9809d369 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Sat, 3 Oct 2026 22:54:59 +0200 Subject: [PATCH] Aegis: only the page's own scripts can reach the wallet relay The isolated-world relay accepted any postMessage carrying the fixed tag "aegis-aegis", including one from a cross-origin iframe (an ad, an embed), and attributed it to the top-level origin and its grants. The tag now carries a per-load random nonce that only the injected main-world bridge knows, and both listeners drop events whose source is not this window. The document_start install path is unchanged. --- bundled-addons/aegis/wallet-inject.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/bundled-addons/aegis/wallet-inject.js b/bundled-addons/aegis/wallet-inject.js index 1a73782d..fbd8be1d 100644 --- a/bundled-addons/aegis/wallet-inject.js +++ b/bundled-addons/aegis/wallet-inject.js @@ -191,13 +191,20 @@ theseus.contextBridge.exposeInMainWorld("wizardconnect", wizardconnect); // to us via `window.postMessage` on a namespaced envelope. This mirrors how // MetaMask and Phantom bridge extension code back to page code. -// Namespace used on the postMessage envelope. Includes the addon id so a -// page that runs multiple dapp-wallet extensions doesn't misroute messages. -const AEGIS_TAG = "aegis-" + theseus.id; +// Namespace used on the postMessage envelope. Includes the addon id plus a +// per-page-load nonce: only the main-world bridge we install below knows +// it, so a cross-origin iframe on the page cannot address the relay. The +// listener additionally requires the event to come from this very window +// (e.source === window) — an iframe's postMessage to its parent has +// e.source === that iframe — so a request can never be attributed to the +// top-level origin by anything but the top-level page's own scripts. The +// old fixed tag let any embedded ad frame call the wallet as the page. +const AEGIS_TAG = "aegis-" + theseus.id + "-" + Array.from(crypto.getRandomValues(new Uint8Array(12)), (b) => b.toString(16).padStart(2, "0")).join(""); const pendingCalls = new Map(); window.addEventListener("message", async (e) => { - const d = e && e.data; - if (!d || d.aegisTag !== AEGIS_TAG) return; + if (!e || e.source !== window || e.origin !== location.origin) return; + const d = e.data; + if (!d || typeof d !== "object" || d.aegisTag !== AEGIS_TAG) return; if (d.kind === "request") { // Forward main-world → isolated-world → addon. try { @@ -229,8 +236,9 @@ const mainWorldSource = `(function () { }); } window.addEventListener("message", (e) => { - const d = e && e.data; - if (!d || d.aegisTag !== TAG) return; + if (!e || e.source !== window) return; + const d = e.data; + if (!d || typeof d !== "object" || d.aegisTag !== TAG) return; if (d.kind === "response") { const p = pending.get(d.id); if (!p) return;