Bug 1407258: Dowgrade document loads with expanded principals to their last sub-principal. r=bz
authorKris Maglione <maglione.k@gmail.com>
Tue, 10 Oct 2017 14:08:47 -0700
changeset 385379 3aad26ddfedf4f2765b121d0aa9032b55c01076c
parent 385378 ec3952ef5411c6b24d181bc6eaf0b35a2cb280fd
child 385453 95824b886c451ae21fa14c3add27c5479bc7381b
push id96004
push usermaglione.k@gmail.com
push dateTue, 10 Oct 2017 21:33:36 +0000
treeherdermozilla-inbound@3aad26ddfedf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1407258
milestone58.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 1407258: Dowgrade document loads with expanded principals to their last sub-principal. r=bz There are currently some corner cases where channels that are eventually loaded into documents (mainly <img src="data:image/svg+xml,") can inherit expanded principals from a caller. Since documents aren't allowed to have expanded principals, this causes crashes. This patch is a short term workaround for the issue, until we have a longer term solution that prevents the channels from inheriting the expanded principals to begin with. MozReview-Commit-ID: JwqqtVynLjj
dom/base/nsDocument.cpp
toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -106,16 +106,17 @@
 #include "nsNetUtil.h"     // for NS_NewURI
 #include "nsIInputStreamChannel.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
+#include "ExpandedPrincipal.h"
 #include "NullPrincipal.h"
 
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMElement.h"
 #include "nsFocusManager.h"
 
 // for radio group stuff
@@ -2481,16 +2482,30 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
 
 already_AddRefed<nsIPrincipal>
 nsDocument::MaybeDowngradePrincipal(nsIPrincipal* aPrincipal)
 {
   if (!aPrincipal) {
     return nullptr;
   }
 
+  // We can't load a document with an expanded principal. If we're given one,
+  // automatically downgrade it to the last principal it subsumes (which is the
+  // extension principal, in the case of extension content scripts).
+  auto* basePrin = BasePrincipal::Cast(aPrincipal);
+  if (basePrin->Is<ExpandedPrincipal>()) {
+    auto* expanded = basePrin->As<ExpandedPrincipal>();
+
+    nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist;
+    MOZ_ALWAYS_SUCCEEDS(expanded->GetWhiteList(&whitelist));
+    MOZ_ASSERT(whitelist->Length() > 0);
+
+    return do_AddRef(whitelist->LastElement().get());
+  }
+
   if (!sChromeInContentPrefCached) {
     sChromeInContentPrefCached = true;
     Preferences::AddBoolVarCache(&sChromeInContentAllowed,
                                  kChromeInContentPref, false);
   }
   if (!sChromeInContentAllowed &&
       nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     // We basically want the parent document here, but because this is very
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
@@ -242,16 +242,22 @@ function toHTML(test, opts) {
  *        A list of test objects, as understood by {@see getElementData}.
  * @param {ElementTestOptions} baseOpts
  *        A base options object, as understood by {@see getElementData},
  *        which represents the default values for injections under this
  *        context.
  */
 function injectElements(tests, baseOpts) {
   window.addEventListener("load", () => {
+    // Basic smoke test to check that SVG images do not try to create a document
+    // with an expanded principal, which would cause a crash.
+    let img = document.createElement("img");
+    img.src = "data:image/svg+xml,%3Csvg%2F%3E";
+    document.body.appendChild(img);
+
     let overrideOpts = opts => Object.assign({}, baseOpts, opts);
     let opts = baseOpts;
 
     // Build the full element with setAttr, then inject.
     for (let test of tests) {
       let {elem, srcElem, src} = createElement(test, opts);
       srcElem.setAttribute(test.srcAttr, src);
       document.body.appendChild(elem);