Bug 1573276 - Always allow localization in error pages r=johannh,peterv
authorAlex Catarineu <acat@torproject.org>
Wed, 25 Sep 2019 10:39:45 +0000
changeset 494993 344ae0abff73dd19d0a2c4d04f22db45c8bd335b
parent 494992 88ed3923442a5527d87df3a05e31c7732239b953
child 494994 90162306864cefa2889b2fe56701c8149ea25433
push id114131
push userdluca@mozilla.com
push dateThu, 26 Sep 2019 09:47:34 +0000
treeherdermozilla-inbound@1dc1a755079a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh, peterv
bugs1573276
milestone71.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1573276 - Always allow localization in error pages r=johannh,peterv Differential Revision: https://phabricator.services.mozilla.com/D43216
browser/base/content/test/about/browser_aboutCertError.js
browser/base/content/test/about/head.js
dom/base/Document.cpp
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/security/nsContentSecurityManager.cpp
parser/htmlparser/nsExpatDriver.cpp
--- a/browser/base/content/test/about/browser_aboutCertError.js
+++ b/browser/base/content/test/about/browser_aboutCertError.js
@@ -503,8 +503,31 @@ add_task(async function checkBadStsCertH
       ok(
         titleContent.endsWith("Risk Ahead"),
         "Warning: Potential Security Risk Ahead"
       );
     }
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 });
+
+add_task(async function checkSandboxedIframe() {
+  info(
+    "Loading a bad sts cert error in a sandboxed iframe and check that the correct headline is shown"
+  );
+  let useFrame = true;
+  let sandboxed = true;
+  let tab = await openErrorPage(BAD_CERT, useFrame, sandboxed);
+  let browser = tab.linkedBrowser;
+
+  let titleContent = await ContentTask.spawn(browser, {}, async function() {
+    // Cannot test for error in the Advanced section since it's currently not present
+    // in a sandboxed iframe.
+    let doc = content.document.querySelector("iframe").contentDocument;
+    let titleText = doc.querySelector(".title-text");
+    return titleText.textContent;
+  });
+  ok(
+    titleContent.endsWith("Security Issue"),
+    "Did Not Connect: Potential Security Issue"
+  );
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
--- a/browser/base/content/test/about/head.js
+++ b/browser/base/content/test/about/head.js
@@ -29,51 +29,54 @@ function getPEMString(cert) {
   var wrapped = derb64.replace(/(\S{64}(?!$))/g, "$1\r\n");
   return (
     "-----BEGIN CERTIFICATE-----\r\n" +
     wrapped +
     "\r\n-----END CERTIFICATE-----\r\n"
   );
 }
 
-function injectErrorPageFrame(tab, src) {
+function injectErrorPageFrame(tab, src, sandboxed) {
   return ContentTask.spawn(
     tab.linkedBrowser,
-    { frameSrc: src },
-    async function({ frameSrc }) {
+    { frameSrc: src, frameSandboxed: sandboxed },
+    async function({ frameSrc, frameSandboxed }) {
       let loaded = ContentTaskUtils.waitForEvent(
         content.wrappedJSObject,
         "DOMFrameContentLoaded"
       );
       let iframe = content.document.createElement("iframe");
       iframe.src = frameSrc;
+      if (frameSandboxed) {
+        iframe.setAttribute("sandbox", "allow-scripts");
+      }
       content.document.body.appendChild(iframe);
       await loaded;
       // We will have race conditions when accessing the frame content after setting a src,
       // so we can't wait for AboutNetErrorLoad. Let's wait for the certerror class to
       // appear instead (which should happen at the same time as AboutNetErrorLoad).
       await ContentTaskUtils.waitForCondition(() =>
         iframe.contentDocument.body.classList.contains("certerror")
       );
     }
   );
 }
 
-async function openErrorPage(src, useFrame) {
+async function openErrorPage(src, useFrame, sandboxed) {
   let dummyPage =
     getRootDirectory(gTestPath).replace(
       "chrome://mochitests/content",
       "https://example.com"
     ) + "dummy_page.html";
 
   let tab;
   if (useFrame) {
     info("Loading cert error page in an iframe");
     tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, dummyPage);
-    await injectErrorPageFrame(tab, src);
+    await injectErrorPageFrame(tab, src, sandboxed);
   } else {
     let certErrorLoaded;
     tab = await BrowserTestUtils.openNewForegroundTab(
       gBrowser,
       () => {
         gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, src);
         let browser = gBrowser.selectedBrowser;
         certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -3738,21 +3738,23 @@ void Document::InitializeLocalization(ns
   mDocumentL10n = l10n;
 }
 
 DocumentL10n* Document::GetL10n() { return mDocumentL10n; }
 
 bool Document::DocumentSupportsL10n(JSContext* aCx, JSObject* aObject) {
   nsCOMPtr<nsIPrincipal> callerPrincipal =
       nsContentUtils::SubjectPrincipal(aCx);
-  return nsContentUtils::PrincipalAllowsL10n(callerPrincipal);
+  nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
+  return nsContentUtils::PrincipalAllowsL10n(
+      callerPrincipal, win ? win->GetDocumentURI() : nullptr);
 }
 
 void Document::LocalizationLinkAdded(Element* aLinkElement) {
-  if (!nsContentUtils::PrincipalAllowsL10n(NodePrincipal())) {
+  if (!nsContentUtils::PrincipalAllowsL10n(NodePrincipal(), GetDocumentURI())) {
     return;
   }
 
   nsAutoString href;
   aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
   // If the link is added after the DocumentL10n instance
   // has been initialized, just pass the resource ID to it.
   if (mDocumentL10n) {
@@ -3780,17 +3782,17 @@ void Document::LocalizationLinkAdded(Ele
       BlockOnload();
     }
 
     mPendingInitialTranslation = true;
   }
 }
 
 void Document::LocalizationLinkRemoved(Element* aLinkElement) {
-  if (!nsContentUtils::PrincipalAllowsL10n(NodePrincipal())) {
+  if (!nsContentUtils::PrincipalAllowsL10n(NodePrincipal(), GetDocumentURI())) {
     return;
   }
 
   nsAutoString href;
   aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
   if (mDocumentL10n) {
     AutoTArray<nsString, 1> resourceIds;
     resourceIds.AppendElement(href);
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -252,16 +252,17 @@
 #include "nsIBrowser.h"
 #include "mozilla/HangAnnotations.h"
 #include "mozilla/Encoding.h"
 #include "nsXULElement.h"
 #include "mozilla/RecordReplay.h"
 #include "nsThreadManager.h"
 #include "nsIBidiKeyboard.h"
 #include "ReferrerInfo.h"
+#include "nsAboutProtocolUtils.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #  undef LoadImage
 #endif
 
 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
                                       const char** next, char16_t* result);
@@ -1665,18 +1666,40 @@ bool nsContentUtils::OfflineAppAllowed(n
     return false;
   }
 
   bool allowed;
   nsresult rv = updateService->OfflineAppAllowed(aPrincipal, &allowed);
   return NS_SUCCEEDED(rv) && allowed;
 }
 
-/* static */
-bool nsContentUtils::PrincipalAllowsL10n(nsIPrincipal* aPrincipal) {
+static bool IsErrorPage(nsIURI* aURI) {
+  if (!aURI) {
+    return false;
+  }
+
+  if (!aURI->SchemeIs("about")) {
+    return false;
+  }
+
+  nsAutoCString name;
+  nsresult rv = NS_GetAboutModuleName(aURI, name);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
+         name.EqualsLiteral("blocked");
+}
+
+/* static */
+bool nsContentUtils::PrincipalAllowsL10n(nsIPrincipal* aPrincipal,
+                                         nsIURI* aDocumentURI) {
+  if (IsErrorPage(aDocumentURI)) {
+    return true;
+  }
+
   // The system principal is always allowed.
   if (IsSystemPrincipal(aPrincipal)) {
     return true;
   }
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, false);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1946,21 +1946,22 @@ class nsContentUtils {
   static bool OfflineAppAllowed(nsIURI* aURI);
 
   /**
    * Check whether an application should be allowed to use offline APIs.
    */
   static bool OfflineAppAllowed(nsIPrincipal* aPrincipal);
 
   /**
-   * Determine whether the principal is allowed access to the localization
-   * system. We don't want the web to ever see this but all our UI including in
-   * content pages should pass this test.
+   * Determine whether the principal or document is allowed access to the
+   * localization system. We don't want the web to ever see this but all our UI
+   * including in content pages should pass this test.
    */
-  static bool PrincipalAllowsL10n(nsIPrincipal* aPrincipal);
+  static bool PrincipalAllowsL10n(nsIPrincipal* aPrincipal,
+                                  nsIURI* aDocumentURI);
 
   /**
    * Increases the count of blockers preventing scripts from running.
    * NOTE: You might want to use nsAutoScriptBlocker rather than calling
    * this directly
    */
   static void AddScriptBlocker();
 
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -281,17 +281,21 @@ static bool IsImageLoadInEditorAppType(n
   return appType == nsIDocShell::APP_TYPE_EDITOR;
 }
 
 static nsresult DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
   // In practice, these DTDs are just used for localization, so applying the
   // same principal check as Fluent.
   if (aLoadInfo->InternalContentPolicyType() ==
       nsIContentPolicy::TYPE_INTERNAL_DTD) {
-    return nsContentUtils::PrincipalAllowsL10n(aLoadInfo->TriggeringPrincipal())
+    RefPtr<Document> doc;
+    aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
+    return nsContentUtils::PrincipalAllowsL10n(
+               aLoadInfo->TriggeringPrincipal(),
+               doc ? doc->GetDocumentURI() : nullptr)
                ? NS_OK
                : NS_ERROR_DOM_BAD_URI;
   }
 
   // This is used in order to allow a privileged DOMParser to parse documents
   // that need to access localization DTDs. We just allow through
   // TYPE_INTERNAL_FORCE_ALLOWED_DTD no matter what the triggering principal is.
   if (aLoadInfo->InternalContentPolicyType() ==
--- a/parser/htmlparser/nsExpatDriver.cpp
+++ b/parser/htmlparser/nsExpatDriver.cpp
@@ -633,43 +633,47 @@ nsresult nsExpatDriver::OpenInputStreamF
 
   nsCOMPtr<nsIChannel> channel;
   if (localURI) {
     localURI.swap(uri);
     rv = NS_NewChannel(getter_AddRefs(channel), uri,
                        nsContentUtils::GetSystemPrincipal(),
                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                        nsIContentPolicy::TYPE_DTD);
+    NS_ENSURE_SUCCESS(rv, rv);
   } else {
     NS_ASSERTION(
         mSink == nsCOMPtr<nsIExpatSink>(do_QueryInterface(mOriginalSink)),
         "In nsExpatDriver::OpenInputStreamFromExternalDTD: "
         "mOriginalSink not the same object as mSink?");
     nsContentPolicyType policyType = nsIContentPolicy::TYPE_INTERNAL_DTD;
-    nsCOMPtr<nsIPrincipal> loadingPrincipal;
     if (mOriginalSink) {
       nsCOMPtr<Document> doc;
       doc = do_QueryInterface(mOriginalSink->GetTarget());
       if (doc) {
-        loadingPrincipal = doc->NodePrincipal();
         if (doc->SkipDTDSecurityChecks()) {
           policyType = nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD;
         }
+        rv = NS_NewChannel(getter_AddRefs(channel), uri, doc,
+                           nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
+                               nsILoadInfo::SEC_ALLOW_CHROME,
+                           policyType);
+        NS_ENSURE_SUCCESS(rv, rv);
       }
     }
-    if (!loadingPrincipal) {
-      loadingPrincipal =
+    if (!channel) {
+      nsCOMPtr<nsIPrincipal> nullPrincipal =
           mozilla::NullPrincipal::CreateWithoutOriginAttributes();
+      rv = NS_NewChannel(getter_AddRefs(channel), uri, nullPrincipal,
+                         nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
+                             nsILoadInfo::SEC_ALLOW_CHROME,
+                         policyType);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
-    rv = NS_NewChannel(getter_AddRefs(channel), uri, loadingPrincipal,
-                       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
-                           nsILoadInfo::SEC_ALLOW_CHROME,
-                       policyType);
   }
-  NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString absURL;
   rv = uri->GetSpec(absURL);
   NS_ENSURE_SUCCESS(rv, rv);
   CopyUTF8toUTF16(absURL, aAbsURL);
 
   channel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
   return channel->Open(aStream);