Theseus: page scans run in an isolated world; install sheet names the risk

The wiz:// page scan ran in the page's own world, where the page can
replace RegExp, querySelectorAll or Set and shape what the wallet is
handed; it now runs in an isolated world of its own and the results
are checked again in main. The extension install sheet now says what a
community extension can reach while the vault is unlocked (passwords
and the wallet), since extensions still run in the main process.
This commit is contained in:
Local Dev 2026-10-04 04:29:02 +02:00
parent f834fbf80a
commit 76d342797b

15
main.js
View file

@ -2804,7 +2804,10 @@ function initAddons() {
// Percent-encoded QR spelling: WIZ://%3FP%3D…
const qrSrc = `\\b${scheme}://(?:%[0-9A-Fa-f]{2}|[A-Za-z0-9._~$+-])+`;
const arg = JSON.stringify({ plainSrc, qrSrc, limit });
const found = await wc.executeJavaScript(`(() => {
// Run in an isolated world of our own (the inject preload uses 999):
// the page shares the DOM but not its globals, so it cannot replace
// RegExp, querySelectorAll or Set to shape what the scan returns.
const found = await wc.executeJavaScriptInIsolatedWorld(1001, [{ code: `(() => {
const { plainSrc, qrSrc, limit } = ${arg};
const out = new Set();
const plain = new RegExp(plainSrc, "gi");
@ -2828,8 +2831,12 @@ function initAddons() {
for (const el of document.querySelectorAll("input,textarea")) scan(el.value);
scan(document.body ? document.body.innerText : "");
return [...out].slice(0, limit);
})()`, true);
const uris = Array.isArray(found) ? found.filter((s) => typeof s === "string") : [];
})()` }], true);
// And checked again here, whatever came back.
const want = scheme.toLowerCase() + "://";
const uris = (Array.isArray(found) ? found : [])
.filter((s) => typeof s === "string" && s.length <= 2048 && s.toLowerCase().startsWith(want))
.slice(0, Math.max(0, Number(limit) || 0));
console.log(`[addons] ${addonId} scanned ${origin || "tab"} for ${scheme}:// — ${uris.length} match(es)`);
return { origin, uris };
},
@ -5692,7 +5699,7 @@ async function installExtensionWithConsent(id, requester) {
? `Update ${card.name || id} ${installed.version} → ${card.latest}?`
: `Install ${card.name || id} ${card.latest}?`,
detail: `${from}Published by ${card.publisher}. Theseus verifies the package signature against ${card.publisher}'s current owner in its own chain index before anything is written.\n\n`
+ `A community extension runs with the same access as any add-on — treat it like a program from that publisher.`,
+ `A community extension runs inside Theseus with the same access as Theseus itself: while your vault is unlocked it can reach your saved passwords and your Aegis wallet. Install it only if you would install a program from this publisher.`,
buttons: ["Install", "Cancel"], defaultId: 1, cancelId: 1, noLink: true,
});
if (response !== 0) return { ok: false, error: "cancelled" };