From caacc07b2d65aa1ba292681cc4f4f607bf9161dc Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Thu, 24 Mar 2022 14:35:35 +0000 (2022-03-24) Subject: [PATCH] CVE-2022-29912 --- browser/actors/AboutReaderParent.jsm | 7 ++ toolkit/components/reader/AboutReader.jsm | 7 +- toolkit/components/reader/ReaderMode.jsm | 82 ++++++++--------------- 3 files changed, 40 insertions(+), 56 deletions(-) diff --git a/browser/actors/AboutReaderParent.jsm b/browser/actors/AboutReaderParent.jsm index 20bb1b3be3..9c5fd812f7 100644 --- a/browser/actors/AboutReaderParent.jsm +++ b/browser/actors/AboutReaderParent.jsm @@ -155,6 +155,13 @@ class AboutReaderParent extends JSWindowActorParent { this.callListeners(message); break; } + + case "RedirectTo": { + gCachedArticles.set(message.data.newURL, message.data.article); + // This is setup as a query so we can navigate the page after we've + // cached the relevant info in the parent. + return true; + } default: this.callListeners(message); diff --git a/toolkit/components/reader/AboutReader.jsm b/toolkit/components/reader/AboutReader.jsm index 4904b525fb..3a1e95411c 100644 --- a/toolkit/components/reader/AboutReader.jsm +++ b/toolkit/components/reader/AboutReader.jsm @@ -743,7 +743,12 @@ AboutReader.prototype = { try { article = await ReaderMode.downloadAndParseDocument(url); } catch (e) { - if (e && e.newURL) { + if (e?.newURL && this._actor) { + await this._actor.sendQuery("RedirectTo", { + newURL: e.newURL, + article: e.article, + }); + let readerURL = "about:reader?url=" + encodeURIComponent(e.newURL); this._win.location.replace(readerURL); return; diff --git a/toolkit/components/reader/ReaderMode.jsm b/toolkit/components/reader/ReaderMode.jsm index 57694c9467..33b80f2c67 100644 --- a/toolkit/components/reader/ReaderMode.jsm +++ b/toolkit/components/reader/ReaderMode.jsm @@ -80,7 +80,7 @@ var ReaderMode = { */ enterReaderMode(docShell, win) { let url = win.document.location.href; - let readerURL = "about:reader?url=" + encodeURIComponent(url); + let originalURL = this.getOriginalUrl(url); let webNav = docShell.QueryInterface(Ci.nsIWebNavigation); let sh = webNav.sessionHistory; if (webNav.canGoForward) { @@ -181,8 +181,8 @@ var ReaderMode = { }, getOriginalUrlObjectForDisplay(url) { - let originalUrl = this.getOriginalUrl(url); - if (originalUrl) { + let originalUrl = this.getOriginalUrl(url); + if (originalUrl) { let uriObj; try { uriObj = Services.uriFixup.createFixupURI( @@ -229,10 +229,11 @@ var ReaderMode = { * @resolves JS object representing the article, or null if no article is found. */ async downloadAndParseDocument(url) { - let doc = await this._downloadDocument(url); - if (!doc) { + let result = await this._downloadDocument(url, docContentType); + if (!result?.doc) { return null; } + let { doc, newURL } = result; if ( !Readerable.shouldCheckUri(doc.documentURIObject) || !Readerable.shouldCheckUri(doc.baseURIObject, true) @@ -241,7 +242,14 @@ var ReaderMode = { return null; } - return this._readerParse(doc); + let article = await this._readerParse(doc); + // If we have to redirect, reject to the caller with the parsed article, + // so we can update the URL before displaying it. + if (newURL) { + return Promise.reject({ newURL, article }); + } + // Otherwise, we can just continue with the article. + return article; }, _downloadDocument(url) { @@ -276,49 +284,7 @@ var ReaderMode = { histogram.add(DOWNLOAD_ERROR_NO_DOC); return; } - - // Manually follow a meta refresh tag if one exists. - let meta = doc.querySelector("meta[http-equiv=refresh]"); - if (meta) { - let content = meta.getAttribute("content"); - if (content) { - let urlIndex = content.toUpperCase().indexOf("URL="); - if (urlIndex > -1) { - let baseURI = Services.io.newURI(url); - let newURI = Services.io.newURI( - content.substring(urlIndex + 4), - null, - baseURI - ); - let newURL = newURI.spec; - let ssm = Services.scriptSecurityManager; - let flags = - ssm.LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT | - ssm.DISALLOW_INHERIT_PRINCIPAL; - try { - ssm.checkLoadURIStrWithPrincipal( - doc.nodePrincipal, - newURL, - flags - ); - } catch (ex) { - let errorMsg = - "Reader mode disallowed meta refresh (reason: " + ex + ")."; - - if (Services.prefs.getBoolPref("reader.errors.includeURLs")) { - errorMsg += " Refresh target URI: '" + newURL + "'."; - } - reject(errorMsg); - return; - } - // Otherwise, pass an object indicating our new URL: - if (!baseURI.equalsExceptRef(newURI)) { - reject({ newURL }); - return; - } - } - } - } + let responseURL = xhr.responseURL; let givenURL = url; // Convert these to real URIs to make sure the escaping (or lack @@ -332,16 +298,22 @@ var ReaderMode = { givenURL = Services.io.newURI(givenURL).specIgnoringRef; } catch (ex) { /* Ignore errors - we'll use what we had before */ + } + if (xhr.responseType != "document") { + let initialText = doc; + let parser = new DOMParser(); + doc = parser.parseFromString(`
`, "text/html");
+          doc.querySelector("pre").textContent = initialText;
         }
 
+        // We treat redirects as download successes here:
+        histogram.add(DOWNLOAD_SUCCESS);
+        let result = { doc };
         if (responseURL != givenURL) {
-          // We were redirected without a meta refresh tag.
-          // Force redirect to the correct place:
-          reject({ newURL: xhr.responseURL });
-          return;
+          result.newURL = xhr.responseURL;
         }
-        resolve(doc);
-        histogram.add(DOWNLOAD_SUCCESS);
+
+        resolve(result);
       };
       xhr.send();
     });
-- 
2.33.0