theseus/addon-inject-preload.js
Local Dev 727fa27ebd WizardConnect "Open in wallet" links that open a new window reach Aegis
Potidaea's "Open in wallet" is <a href="wiz://…" target="_blank"
rel="noopener noreferrer">, a shape many dapps use. A new-window wiz:// link
goes through the window-open handler, which credits a pairing to the page
only through the request's referrer, so it can tell the page's own link
from one in a frame. noreferrer strips the referrer, so the link was
dropped and the wallet never heard of it.

The inject preload, which runs in a tab's top document only, now hands a
trusted click on a wiz:// link straight to main, and main accepts it only
from the tab's main frame before routing it like any pairing link. Links
inside iframes still go the old way and are still refused without a
referrer.
2026-10-22 19:15:05 +03:00

65 lines
3.3 KiB
JavaScript

// Session-wide preload that runs every page-inject add-on's bridge script in
// the isolated world of tabs whose URL matches the add-on's declared origin
// patterns. Registered via session.defaultSession.setPreloads in main.js
// alongside bcnr-preload.js.
//
// The decision of WHICH scripts apply is made in main against the sender's
// committed URL, not against anything the page can influence. Each script
// gets a `theseus` object scoped to its add-on id:
// theseus.contextBridge — expose an API into the page's main world
// theseus.invoke(msg, payload) — call the add-on's onMessage(msg) handler
// theseus.origin — the page origin main will show the user
// theseus.evalInPage(code) — run code in the page's main world, resolve
// its value (cookie-consent rules need it)
// plus a `require` that only resolves "electron" so scripts written in the
// ordinary preload idiom keep working.
const { contextBridge, ipcRenderer, webFrame } = require("electron");
// Web tabs run preloads in iframes too (nodeIntegrationInSubFrames, for the
// password hooks). Add-on page scripts (wallet providers, consent rules)
// stay top-frame only, as they always were: main decides them from the
// tab's URL, not the frame's, and must never put them into third-party frames.
if (window.top !== window) return;
let injections = [];
try { injections = ipcRenderer.sendSync("addon-inject-scripts", location.href) || []; }
catch (e) { console.warn("[theseus] add-on inject query failed:", e?.message || e); }
for (const inj of injections) {
const id = String(inj.id);
const theseus = Object.freeze({
id,
origin: inj.origin,
contextBridge,
invoke: (msg, payload) => ipcRenderer.invoke("addon-page-msg", id, String(msg), payload),
evalInPage: (code) => webFrame.executeJavaScript(String(code)),
});
const scopedRequire = (name) => {
if (name === "electron") return { contextBridge };
throw new Error(`addon inject scripts may only require("electron") — got ${name}`);
};
try {
new Function("theseus", "require", inj.source)(theseus, scopedRequire);
} catch (e) {
console.warn(`[theseus] add-on "${id}" page-inject failed:`, e?.message || e);
}
}
// wiz:// links (WizardConnect pairing, "Open in wallet"). A same-tab click is
// caught by main on will-navigate; one that opens a new window
// (target="_blank") reaches main's window-open handler, which can tell the
// page's own link from a frame's only by the referrer, and rel="noreferrer",
// common on dapps, strips it. So those clicks were dropped and the wallet
// never saw them. A click the user really made on such a link in this, the
// top document, goes straight to main instead; main checks it came from the
// tab's main frame and routes it like any other pairing link.
document.addEventListener("click", (e) => {
if (!e.isTrusted || e.button !== 0) return;
// composedPath reaches into shadow roots, where web-component buttons live.
const a = (typeof e.composedPath === "function" ? e.composedPath() : [e.target])
.find((n) => n && n.tagName === "A" && n.hasAttribute && n.hasAttribute("href"));
if (!a) return;
const href = String(a.getAttribute("href") || "").trim();
if (!/^wiz:/i.test(href) || href.length > 4096) return;
e.preventDefault();
ipcRenderer.send("wiz-link", href);
}, true);