From bc1b5fc0f3eb80d0af7d9e7e6f3a742b35268da7 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Sun, 4 Oct 2026 04:23:54 +0200 Subject: [PATCH] Theseus: add-on panels never leave their own page The right-hand panel had neither a will-navigate nor a window-open handler, so a link in a panel navigated the privileged view itself to a remote page that kept sidebar-preload, and window.open made a bare window with it; panel events were routed by the selected panel id, so Aegis's state (every address, balances, WizardConnect sessions) would then have reached that page. Both panels now open web links as tabs and refuse to navigate away from file://, and events go only to a view that has the add-on's own page loaded. --- main.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/main.js b/main.js index 0772bbda..ec853ec1 100644 --- a/main.js +++ b/main.js @@ -2620,10 +2620,14 @@ function initAddons() { if (t.addonId !== addonId) continue; try { t.view?.webContents?.send("addon-event", msg, payload); } catch {} } - if (leftPanel && leftPanelLoadedId && leftPanelLoadedId.startsWith(addonId + ":")) { + // Delivered by what the view has actually loaded, not only by which + // panel was last selected — an add-on's state (addresses, balances) + // must never reach a document that is not that add-on's. + if (leftPanel && leftPanelLoadedId && leftPanelLoadedId.startsWith(addonId + ":") && addonIdForSender(leftPanel.webContents) === addonId) { try { leftPanel.webContents.send("addon-event", msg, payload); } catch {} } if (!sidebar || !sidebarActivePanelId || !sidebarActivePanelId.startsWith(addonId + ":")) return; + if (addonIdForSender(sidebar.webContents) !== addonId) return; try { sidebar.webContents.send("addon-event", msg, payload); } catch {} }, hostRequire: (name) => require(name), @@ -2959,6 +2963,22 @@ function initAddons() { // Given a webContents sender URL, work out which add-on folder it lives in. // Used to gate storage IPC — a page hosted inside addons// can only touch // its own store. +// Add-on panels carry sidebar-preload. A link in a panel used to navigate +// the panel itself to a remote page (keeping that preload), and window.open +// made a bare BrowserWindow with it. Web links open as tabs instead, and a +// panel never leaves file://. +function lockPanelView(view) { + view.webContents.setWindowOpenHandler(({ url }) => { + if (url && /^(?:https?|bns):/i.test(url)) createTab(url); + return { action: "deny" }; + }); + view.webContents.on("will-navigate", (e) => { + const url = String(e.url || ""); + if (/^file:/i.test(url)) return; + e.preventDefault(); + if (/^(?:https?|bns):/i.test(url)) createTab(url); + }); +} function addonIdForSender(sender) { try { const u = new URL(sender.getURL()); @@ -4400,6 +4420,7 @@ function createWindow() { // panel loaded into this view (Aegis, Screenshot, etc.) picks up the // brand scrollbar the moment its DOM is ready. sidebar = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "sidebar-preload.js") } }); + lockPanelView(sidebar); win.contentView.addChildView(sidebar); styleScrollbars(sidebar.webContents); sidebar.setVisible(false); @@ -4433,10 +4454,7 @@ function createWindow() { quickPanel.setVisible(false); // Left add-on panels: same preload and IPC surface as the right sidebar. leftPanel = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "sidebar-preload.js") } }); - leftPanel.webContents.setWindowOpenHandler(({ url }) => { - if (url && /^(?:https?|bns):/i.test(url)) createTab(url); - return { action: "deny" }; - }); + lockPanelView(leftPanel); try { leftPanel.setBackgroundColor(nativeTheme.shouldUseDarkColors ? "#0b0e14" : "#ffffff"); } catch {} win.contentView.addChildView(leftPanel); styleScrollbars(leftPanel.webContents);