richard pushed to branch tor-browser-115.4.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits: f83bd860 by Henry Wilkes at 2023-11-06T23:18:21+00:00 fixup! Bug 27476: Implement about:torconnect captive portal within Tor Browser
Bug 42184: Make torconnect redirect compatible with blank home page.
- - - - - f0ddec98 by Henry Wilkes at 2023-11-06T23:18:28+00:00 fixup! Bug 40597: Implement TorSettings module
Bug 42184: Split out URI fixup logic to fixupURIs and simplify. This method is used in TorConnectParent to fixup the home page preference.
- - - - -
4 changed files:
- browser/components/torconnect/TorConnectChild.sys.mjs - browser/components/torconnect/TorConnectParent.sys.mjs - browser/components/torconnect/content/aboutTorConnect.js - browser/modules/TorConnect.sys.mjs
Changes:
===================================== browser/components/torconnect/TorConnectChild.sys.mjs ===================================== @@ -2,4 +2,78 @@
import { RemotePageChild } from "resource://gre/actors/RemotePageChild.sys.mjs";
-export class TorConnectChild extends RemotePageChild {} +export class TorConnectChild extends RemotePageChild { + /** + * Whether we have redirected the page (after bootstrapping) or not. + * + * @type {boolean} + */ + #redirected = false; + + /** + * If bootstrapping is complete, or TorConnect is disabled, we redirect the + * page. + */ + async #maybeRedirect() { + if (await this.sendQuery("torconnect:should-show")) { + // Enabled and not yet bootstrapped. + return; + } + if (this.#redirected) { + return; + } + this.#redirected = true; + + const redirect = new URLSearchParams( + new URL(this.contentWindow.document.location.href).search + ).get("redirect"); + + // Fallback in error cases: + let replaceURI = "about:tor"; + try { + const url = new URL( + redirect + ? decodeURIComponent(redirect) + : // NOTE: We expect no redirect when address is entered manually, or + // about:torconnect is opened from preferences or urlbar. + // Go to the home page. + await this.sendQuery("torconnect:home-page") + ); + // Do not allow javascript URI. See tor-browser#41766 + if ( + ["about:", "file:", "https:", "http:"].includes(url.protocol) || + // Allow blank page. See tor-browser#42184. + // Blank page's are given as a chrome URL rather than "about:blank". + url.href === "chrome://browser/content/blanktab.html" + ) { + replaceURI = url.href; + } else { + console.error(`Scheme is not allowed "${redirect}"`); + } + } catch { + console.error(`Invalid redirect URL "${redirect}"`); + } + + // Replace the destination to prevent "about:torconnect" entering the + // history. + // NOTE: This is done here, in the window actor, rather than in content + // because we have the privilege to redirect to a "chrome:" uri here (for + // when the HomePage is set to be blank). + this.contentWindow.location.replace(replaceURI); + } + + actorCreated() { + super.actorCreated(); + // about:torconnect could need to be immediately redirected. E.g. if it is + // reached after bootstrapping. + this.#maybeRedirect(); + } + + receiveMessage(message) { + super.receiveMessage(message); + + if (message.name === "torconnect:state-change") { + this.#maybeRedirect(); + } + } +}
===================================== browser/components/torconnect/TorConnectParent.sys.mjs ===================================== @@ -1,5 +1,7 @@ // Copyright (c) 2021, The Tor Project, Inc.
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm"); import { InternetStatus, @@ -15,6 +17,12 @@ import {
const BroadcastTopic = "about-torconnect:broadcast";
+const lazy = {}; + +XPCOMUtils.defineLazyModuleGetters(lazy, { + HomePage: "resource:///modules/HomePage.jsm", +}); + /* This object is basically a marshalling interface between the TorConnect module and a particular about:torconnect page @@ -167,6 +175,11 @@ export class TorConnectParent extends JSWindowActorParent {
async receiveMessage(message) { switch (message.name) { + case "torconnect:should-show": + return Promise.resolve(TorConnect.shouldShowTorConnect); + case "torconnect:home-page": + // If there are multiple home pages, just load the first one. + return Promise.resolve(TorConnect.fixupURIs(lazy.HomePage.get())[0]); case "torconnect:set-quickstart": TorSettings.quickstart.enabled = message.data; TorSettings.saveToPrefs().applySettings();
===================================== browser/components/torconnect/content/aboutTorConnect.js ===================================== @@ -136,10 +136,6 @@ class AboutTorConnect { tryBridgeButton: document.querySelector(this.selectors.buttons.tryBridge), });
- // a redirect url can be passed as a query parameter for the page to - // forward us to once bootstrap completes (otherwise the window will just close) - redirect = null; - uiState = { currentState: UIStates.ConnectToTor, allowAutomaticLocation: true, @@ -425,11 +421,6 @@ class AboutTorConnect { this.setLongText(TorStrings.settings.torPreferencesDescription); this.setProgress("", showProgressbar, 100); this.hideButtons(); - - // redirects page to the requested redirect url, removes about:torconnect - // from the page stack, so users cannot accidentally go 'back' to the - // now unresponsive page - window.location.replace(this.redirect); }
update_Disabled(state) { @@ -822,23 +813,6 @@ class AboutTorConnect { }
async init() { - // if the user gets here manually or via the button in the urlbar - // then we will redirect to about:tor - this.redirect = "about:tor"; - - // see if a user has a final destination after bootstrapping - let params = new URLSearchParams(new URL(document.location.href).search); - if (params.has("redirect")) { - try { - const redirect = new URL(decodeURIComponent(params.get("redirect"))); - if (/^(?:https?|about):$/.test(redirect.protocol)) { - this.redirect = redirect.href; - } - } catch (e) { - console.error(e, `Invalid redirect URL "${params.get("redirect")}"!`); - } - } - let args = await RPMSendQuery("torconnect:get-init-args");
// various constants
===================================== browser/modules/TorConnect.sys.mjs ===================================== @@ -1156,67 +1156,54 @@ export const TorConnect = (() => { return `about:torconnect?redirect=${encodeURIComponent(url)}`; },
+ /** + * Convert the given object into a list of valid URIs. + * + * The object is either from the user's homepage preference (which may + * contain multiple domains separated by "|") or uris passed to the browser + * via command-line. + * + * @param {string|string[]} uriVariant - The string to extract uris from. + * + * @return {string[]} - The array of uris found. + */ + fixupURIs(uriVariant) { + let uriArray; + if (typeof uriVariant === "string") { + uriArray = uriVariant.split("|"); + } else if ( + Array.isArray(uriVariant) && + uriVariant.every(entry => typeof entry === "string") + ) { + uriArray = uriVariant; + } else { + // about:tor as safe fallback + console.error( + `TorConnect: received unknown variant '${JSON.stringify(uriVariant)}'` + ); + uriArray = ["about:tor"]; + } + + // Attempt to convert user-supplied string to a uri, fallback to + // about:tor if cannot convert to valid uri object + return uriArray.map( + uriString => + Services.uriFixup.getFixupURIInfo( + uriString, + Ci.nsIURIFixup.FIXUP_FLAG_NONE + ).preferredURI?.spec ?? "about:tor" + ); + }, + // called from browser.js on browser startup, passed in either the user's homepage(s) // or uris passed via command-line; we want to replace them with about:torconnect uris // which redirect after bootstrapping getURIsToLoad(uriVariant) { - // convert the object we get from browser.js - let uriStrings = (v => { - // an interop array - if (v instanceof Ci.nsIArray) { - // Transform the nsIArray of nsISupportsString's into a JS Array of - // JS strings. - return Array.from( - v.enumerate(Ci.nsISupportsString), - supportStr => supportStr.data - ); - // an interop string - } else if (v instanceof Ci.nsISupportsString) { - return [v.data]; - // a js string - } else if (typeof v === "string") { - return v.split("|"); - // a js array of js strings - } else if ( - Array.isArray(v) && - v.reduce((allStrings, entry) => { - return allStrings && typeof entry === "string"; - }, true) - ) { - return v; - } - // about:tor as safe fallback - console.log( - `TorConnect: getURIsToLoad() received unknown variant '${JSON.stringify( - v - )}'` - ); - return ["about:tor"]; - })(uriVariant); - - // will attempt to convert user-supplied string to a uri, fallback to about:tor if cannot convert - // to valid uri object - let uriStringToUri = uriString => { - const fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_NONE; - let uri = Services.uriFixup.getFixupURIInfo( - uriString, - fixupFlags - ).preferredURI; - return uri ? uri : Services.io.newURI("about:tor"); - }; - let uris = uriStrings.map(uriStringToUri); - - // assume we have a valid uri and generate an about:torconnect redirect uri - let redirectUrls = uris.map(uri => this.getRedirectURL(uri.spec)); - + const uris = this.fixupURIs(uriVariant); console.log( - `TorConnect: Will load after bootstrap => [${uris - .map(uri => { - return uri.spec; - }) - .join(", ")}]` + `TorConnect: Will load after bootstrap => [${uris.join(", ")}]` ); - return redirectUrls; + return uris.map(uri => this.getRedirectURL(uri)); }, }; return retval;
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/2b8d06d...