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.
This commit is contained in:
Local Dev 2026-10-03 22:54:59 +02:00
parent 206f54edd2
commit f57cf4c894

View file

@ -191,13 +191,20 @@ theseus.contextBridge.exposeInMainWorld("wizardconnect", wizardconnect);
// to us via `window.postMessage` on a namespaced envelope. This mirrors how // to us via `window.postMessage` on a namespaced envelope. This mirrors how
// MetaMask and Phantom bridge extension code back to page code. // MetaMask and Phantom bridge extension code back to page code.
// Namespace used on the postMessage envelope. Includes the addon id so a // Namespace used on the postMessage envelope. Includes the addon id plus a
// page that runs multiple dapp-wallet extensions doesn't misroute messages. // per-page-load nonce: only the main-world bridge we install below knows
const AEGIS_TAG = "aegis-" + theseus.id; // 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(); const pendingCalls = new Map();
window.addEventListener("message", async (e) => { window.addEventListener("message", async (e) => {
const d = e && e.data; if (!e || e.source !== window || e.origin !== location.origin) return;
if (!d || d.aegisTag !== AEGIS_TAG) return; const d = e.data;
if (!d || typeof d !== "object" || d.aegisTag !== AEGIS_TAG) return;
if (d.kind === "request") { if (d.kind === "request") {
// Forward main-world → isolated-world → addon. // Forward main-world → isolated-world → addon.
try { try {
@ -229,8 +236,9 @@ const mainWorldSource = `(function () {
}); });
} }
window.addEventListener("message", (e) => { window.addEventListener("message", (e) => {
const d = e && e.data; if (!e || e.source !== window) return;
if (!d || d.aegisTag !== TAG) return; const d = e.data;
if (!d || typeof d !== "object" || d.aegisTag !== TAG) return;
if (d.kind === "response") { if (d.kind === "response") {
const p = pending.get(d.id); const p = pending.get(d.id);
if (!p) return; if (!p) return;