From fcd7c4aef0c4b81dafd90ce38f41918ac58da139 Mon Sep 17 00:00:00 2001 From: hackademix Date: Sun, 29 Sep 2019 00:50:43 +0200 Subject: Replace cookie-based hacks with synchronous messaging (currently shimmed) to retrieve fallback and per-tab restriction policies. --- src/bg/RequestGuard.js | 6 +++- src/bg/main.js | 22 +++++++------ src/content/content.js | 9 ++---- src/content/staticNS.js | 82 ++++++++++--------------------------------------- 4 files changed, 36 insertions(+), 83 deletions(-) diff --git a/src/bg/RequestGuard.js b/src/bg/RequestGuard.js index 8d30fe7..3936df9 100644 --- a/src/bg/RequestGuard.js +++ b/src/bg/RequestGuard.js @@ -287,8 +287,12 @@ var RequestGuard = (() => { if (policyType) { let {url, originUrl, documentUrl} = request; let isFetch = "fetch" === policyType; + if ((isFetch || "frame" === policyType) && - (((isFetch && !originUrl || url === originUrl) && originUrl === documentUrl + (((isFetch && (!originUrl || + browser.runtime.onSyncMessage && + url.includes(browser.runtime.onSyncMessage.ENDPOINT_PREFIX) + ) || url === originUrl) && originUrl === documentUrl // some extensions make them both undefined, // see https://github.com/eight04/image-picka/issues/150 ) || diff --git a/src/bg/main.js b/src/bg/main.js index ed53603..9c30d4f 100644 --- a/src/bg/main.js +++ b/src/bg/main.js @@ -141,24 +141,26 @@ }, fetchChildPolicy({url, contextUrl}, sender) { - if (!url) url = sender.url; let {tab} = sender; - let tabUrl = tab.url; - if (!contextUrl) contextUrl = tabUrl; - + let topUrl = tab.url || TabCache.get(tab.id); let policy = !Sites.isInternal(url) && ns.isEnforced(tab.id) ? ns.policy : null; - let permissions = Permissions.ALL; + let permissions, unrestricted, cascaded; if (policy) { let perms = policy.get(url, contextUrl).perms; - if (tabUrl && ns.sync.cascadeRestrictions) { - perms = policy.cascadeRestrictions(perms, tabUrl); + cascaded = ns.sync.cascadeRestrictions; + if (topUrl && cascaded) { + perms = policy.cascadeRestrictions(perms, topUrl); } permissions = perms.dry(); - } // otherwise either internal URL or unrestricted - - return {permissions}; + } else { + // otherwise either internal URL or unrestricted + permissions = new Permissions(Permissions.ALL); + unrestricted = true; + cascaded = false; + } + return {permissions, unrestricted, cascaded}; }, async openStandalonePopup() { diff --git a/src/content/content.js b/src/content/content.js index e7fb331..eabf7c3 100644 --- a/src/content/content.js +++ b/src/content/content.js @@ -58,10 +58,6 @@ var notifyPage = async () => { debug("Page %s shown, %s", document.URL, document.readyState); if (document.readyState === "complete") { try { - if (!("canScript" in ns)) { - ns.fetchPolicy(); - return; - } await Messages.send("pageshow", {seen: seen.list, canScript: ns.canScript}); return true; } catch (e) { @@ -74,8 +70,6 @@ var notifyPage = async () => { return false; } -notifyPage(); - window.addEventListener("pageshow", notifyPage); ns.on("capabilities", () => { @@ -105,3 +99,6 @@ ns.on("capabilities", () => { notifyPage(); }); + +ns.fetchPolicy(); +notifyPage(); diff --git a/src/content/staticNS.js b/src/content/staticNS.js index 7959341..bf91972 100644 --- a/src/content/staticNS.js +++ b/src/content/staticNS.js @@ -33,78 +33,27 @@ backlog.add(eventName); }, - async fetchPolicy() { - let policy = await Messages.send("fetchChildPolicy", {url: document.URL}); - if (!policy) { - debug(`No answer to fetchChildPolicy message. This should not be happening.`); - return false; - } - this.setup(policy.permissions, policy.MARKER, true); - return true; + fetchPolicy() { + let url = document.URL; + let policy = browser.runtime.sendSyncMessage( + {id: "fetchPolicy", url, contextUrl: document.URL}); + if (!policy) { + debug(`No answer to fetchPolicy message. This should not be happening.`); + return false; + } + this.setup(policy); + return true; }, - setup(permissions, MARKER, fetched = false) { - this.config.permissions = permissions; - - // ugly hack: since now we use registerContentScript instead of the - // filterRequest dynamic script injection hack, we use a session cookie - // to store per-tab information, erasing it as soon as we see it - // (before any content can access it) + setup(policy) { + this.policy = policy; - let checkUnrestricted = challenge => sha256(`${MARKER}:${challenge}`); - - if ((this.config.MARKER = MARKER) && permissions) { - let cookieRx = new RegExp(`(?:^|;\\s*)(${MARKER}(?:_\\d+){2})=([^;]*)`); - let match = document.cookie.match(cookieRx); - if (match) { - let [cookie, cookieName, cookieValue] = match; - // delete cookie NOW - document.cookie = `${cookieName}=;expires=${new Date(Date.now() - 31536000000).toGMTString()}`; - try { - this.config.tabInfo = JSON.parse(decodeURIComponent(cookieValue)); - } catch (e) { - error(e); - } - } else if (UA.isMozilla && window !== window.top) { - // The cookie hack won't work for non-HTTP subframes (issue #48), - // or the cookie might have been deleted in a race condition, - // so here we try to check the parent - let checkParent = null; - try { - checkParent = parent.wrappedJSObject.checkNoScriptUnrestricted; - } catch (e) { - // may throw a SecurityException for cross-origin wrappedJSObject access - } - if (typeof checkParent === "function") { - try { - let challenge = uuid(); - let unrestricted = checkParent(challenge) === checkUnrestricted(challenge); - this.config.tabInfo = {unrestricted, inherited: true}; - } catch (e) { - debug("Exception thrown while checking parent unrestricted tab marker. Something fishy going on...") - error(e); - } - } - } - } - - if (!this.config.permissions || this.config.tabInfo.unrestricted) { - exportFunction(checkUnrestricted, window, {defineAs: "checkNoScriptUnrestricted"}); - debug("%s is loading unrestricted by user's choice (%o).", document.URL, this.config); + if (!policy.permissions || policy.unrestricted) { this.allows = () => true; this.capabilities = Object.assign( new Set(["script"]), { has() { return true; } }); } else { - if (!fetched) { - let hostname = window.location.hostname; - if (hostname && hostname.startsWith("[")) { - // WebExt match patterns don't seem to support IPV6 (Firefox 63)... - debug("Ignoring child policy setup parameters for IPV6 address %s, forcing IPC...", hostname); - this.fetchPolicy(); - return; - } - } - let perms = this.config.permissions; + let perms = policy.permissions; this.capabilities = new Set(perms.capabilities); new DocumentCSP(document).apply(this.capabilities, this.embeddingDocument); } @@ -112,7 +61,8 @@ this.canScript = this.allows("script"); this.fire("capabilities"); }, - config: { permissions: null, tabInfo: {}, MARKER: "" }, + + policy: null, allows(cap) { return this.capabilities && this.capabilities.has(cap); -- cgit v1.2.3