From 96ec2c2a6ca8e86fd068eb4eb2082aa0339e1040 Mon Sep 17 00:00:00 2001 From: hackademix Date: Mon, 29 Aug 2022 23:26:32 +0200 Subject: [PATCH] More precise tracking of implicit origins in tab URLs. --- src/bg/TabGuard.js | 90 ++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/src/bg/TabGuard.js b/src/bg/TabGuard.js index e97967f..6480b87 100644 --- a/src/bg/TabGuard.js +++ b/src/bg/TabGuard.js @@ -103,48 +103,70 @@ var TabGuard = (() => { // we suspect tabs which 1) have not been removed/discarded, 2) are restricted by policy, 3) can run JavaScript let suspiciousTabs = [...ties].map(TabCache.get).filter( - tab => tab && !tab.discarded && ns.isEnforced(tab.tabId) && - (tab.url === "about:blank" || ns.policy.can(tab.url, "script")) // TODO: replace about:blank with actual document.domain / window.opener by injecting a content script + tab => tab && !tab.discarded && ns.isEnforced(tab.id) && + (!(tab._isExplicitOrigin = tab._isExplicitOrigin || /^(?:https?|ftps?|file):/.test(tab.url)) || ns.policy.can(tab.url, "script")) ); - let legitDomains = allowedGroups[tabDomain] || new Set([tabDomain]); + return suspiciousTabs.length > 0 && (async () => { - let otherDomains = new Set(suspiciousTabs.map(tab => getDomain(tab.url)).filter(d => d && !legitDomains.has(d))); - if (otherDomains.size === 0) return; // no cross-site ties, no party + let suspiciousDomains = []; + await Promise.all(suspiciousTabs.map(async (tab) => { + if (!tab._isExplicitOrigin) { // e.g. about:blank + // let's try retrieving actual origin + tab._externalUrl = tab.url; + tab._isExplicitOrigin = true; + try { + tab.url = await browser.tabs.executeScript(tab.id, { + runAt: "document_start", + code: "window.origin === 'null' ? window.location.href : window.origin" + }); + } catch (e) { + debug(e); + } + debug(`Real origin for ${tab._externalUrl} (tab ${tab.id}) is ${tab.url}.`); + if (!ns.policy.can(tab.url, "script")) return; + } + suspiciousDomains.push(getDomain(tab.url)); + })); - if (!requestHeaders.some(h => AUTH_HEADERS_RX.test(h.name))) return; // no auth, no party + let legitDomains = allowedGroups[tabDomain] || new Set([tabDomain]); + let otherDomains = new Set(suspiciousDomains.filter(d => d && !legitDomains.has(d))); + if (otherDomains.size === 0) return; // no cross-site ties, no party - // danger zone + if (!requestHeaders.some(h => AUTH_HEADERS_RX.test(h.name))) return; // no auth, no party - let filterAuth = () => { - requestHeaders = requestHeaders.filter(h => !AUTH_HEADERS_RX.test(h.name)); - debug("[TabGuard] Removing auth headers from %o (%o)", request, requestHeaders); - return {requestHeaders}; - }; + // danger zone - let quietDomains = filteredGroups[tabDomain]; - if (mainFrame) { - let mustPrompt = (!quietDomains || [...otherDomains].some(d => !quietDomains.has(d))); - if (mustPrompt) { - return (async () => { - let options = [ - {label: _("TabGuard_optAnonymize"), checked: true}, - {label: _("TabGuard_optAllow")}, - ]; - let ret = await Prompts.prompt({ - title: _("TabGuard_title"), - message: _("TabGuard_message", [tabDomain, [...otherDomains].join(", ")]), - options}); - if (ret.button !== 0) return {cancel: true}; - let list = ret.option === 0 ? filteredGroups : allowedGroups; - otherDomains.add(tabDomain); - for (let d of otherDomains) list[d] = otherDomains; - return list === filteredGroups ? filterAuth() : null; - })(); + let filterAuth = () => { + requestHeaders = requestHeaders.filter(h => !AUTH_HEADERS_RX.test(h.name)); + debug("[TabGuard] Removing auth headers from %o (%o)", request, requestHeaders); + return {requestHeaders}; + }; + + let quietDomains = filteredGroups[tabDomain]; + if (mainFrame) { + let mustPrompt = (!quietDomains || [...otherDomains].some(d => !quietDomains.has(d))); + if (mustPrompt) { + return (async () => { + let options = [ + {label: _("TabGuard_optAnonymize"), checked: true}, + {label: _("TabGuard_optAllow")}, + ]; + let ret = await Prompts.prompt({ + title: _("TabGuard_title"), + message: _("TabGuard_message", [tabDomain, [...otherDomains].join(", ")]), + options}); + if (ret.button !== 0) return {cancel: true}; + let list = ret.option === 0 ? filteredGroups : allowedGroups; + otherDomains.add(tabDomain); + for (let d of otherDomains) list[d] = otherDomains; + return list === filteredGroups ? filterAuth() : null; + })(); + } } - } - let mustFilter = mainFrame || quietDomains && [...otherDomains].some(d => quietDomains.has(d)) - return mustFilter ? filterAuth() : null; + let mustFilter = mainFrame || quietDomains && [...otherDomains].some(d => quietDomains.has(d)) + return mustFilter ? filterAuth() : null; + })(); }, postCheck(request) { let {requestId, tabId} = request;