Bug 1515863, r=ckerschb
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Fri, 21 Dec 2018 11:56:47 +0000
changeset 510147 25334f588aa4b005e7a96d975dd14831fb738a24
parent 510146 c6dc02dcffe28a80ad6e27f0e0be13b91a932496
child 510148 8686beefb44f25a78bb91bc6d5a9e5582f9ffa9a
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb
bugs1515863
milestone66.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 1515863, r=ckerschb Differential Revision: https://phabricator.services.mozilla.com/D15221
browser/base/content/utilityOverlay.js
caps/BasePrincipal.cpp
caps/BasePrincipal.h
caps/ContentPrincipal.cpp
caps/ContentPrincipal.h
caps/nsIScriptSecurityManager.idl
caps/nsScriptSecurityManager.cpp
dom/security/test/csp/browser.ini
dom/security/test/csp/browser_test_uir_optional_clicks.js
dom/security/test/csp/file_csp_meta_uir.html
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -334,17 +334,17 @@ function openLinkIn(url, where, params) 
   // can not do it for NullPrincipals since NullPrincipals are only
   // identical if they actually are the same object (See Bug: 1346759)
   function useOAForPrincipal(principal) {
     if (principal && principal.isCodebasePrincipal) {
       let attrs = {
         userContextId: aUserContextId,
         privateBrowsingId: aIsPrivate || (w && PrivateBrowsingUtils.isWindowPrivate(w)),
       };
-      return Services.scriptSecurityManager.createCodebasePrincipal(principal.URI, attrs);
+      return Services.scriptSecurityManager.principalWithOA(principal, attrs);
     }
     return principal;
   }
   aPrincipal = useOAForPrincipal(aPrincipal);
   aTriggeringPrincipal = useOAForPrincipal(aTriggeringPrincipal);
 
   if (!w || where == "window") {
     let features = "chrome,dialog=no,all";
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -489,15 +489,31 @@ void BasePrincipal::FinishInit(const nsA
   nsAutoCString originSuffix;
   mOriginAttributes.CreateSuffix(originSuffix);
   mOriginSuffix = NS_Atomize(originSuffix);
 
   MOZ_ASSERT(!aOriginNoSuffix.IsEmpty());
   mOriginNoSuffix = NS_Atomize(aOriginNoSuffix);
 }
 
+void BasePrincipal::FinishInit(BasePrincipal* aOther,
+                               const OriginAttributes& aOriginAttributes) {
+  mInitialized = true;
+  mOriginAttributes = aOriginAttributes;
+
+  // First compute the origin suffix since it's infallible.
+  nsAutoCString originSuffix;
+  mOriginAttributes.CreateSuffix(originSuffix);
+  mOriginSuffix = NS_Atomize(originSuffix);
+
+  mOriginNoSuffix = aOther->mOriginNoSuffix;
+  mHasExplicitDomain = aOther->mHasExplicitDomain;
+  mCSP = aOther->mCSP;
+  mPreloadCSP = aOther->mPreloadCSP;
+}
+
 bool SiteIdentifier::Equals(const SiteIdentifier& aOther) const {
   MOZ_ASSERT(IsInitialized());
   MOZ_ASSERT(aOther.IsInitialized());
   return mPrincipal->FastEquals(aOther.mPrincipal);
 }
 
 }  // namespace mozilla
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -236,21 +236,23 @@ class BasePrincipal : public nsJSPrincip
   // Internal, side-effect-free check to determine whether the concrete
   // principal would allow the load ignoring any common behavior implemented in
   // BasePrincipal::CheckMayLoad.
   virtual bool MayLoadInternal(nsIURI* aURI) = 0;
   friend class ::ExpandedPrincipal;
 
   void SetHasExplicitDomain() { mHasExplicitDomain = true; }
 
-  // This function should be called as the last step of the initialization of
-  // the principal objects.  It's typically called as the last step from the
-  // Init() method of the child classes.
+  // Either of these functions should be called as the last step of the
+  // initialization of the principal objects.  It's typically called as the
+  // last step from the Init() method of the child classes.
   void FinishInit(const nsACString& aOriginNoSuffix,
                   const OriginAttributes& aOriginAttributes);
+  void FinishInit(BasePrincipal* aOther,
+                  const OriginAttributes& aOriginAttributes);
 
   nsCOMPtr<nsIContentSecurityPolicy> mCSP;
   nsCOMPtr<nsIContentSecurityPolicy> mPreloadCSP;
 
  private:
   static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(
       nsIURI* aURI, const OriginAttributes& aAttrs,
       const nsACString& aOriginNoSuffix);
--- a/caps/ContentPrincipal.cpp
+++ b/caps/ContentPrincipal.cpp
@@ -76,16 +76,28 @@ nsresult ContentPrincipal::Init(nsIURI* 
       !hasFlag);
 
   mCodebase = aCodebase;
   FinishInit(aOriginNoSuffix, aOriginAttributes);
 
   return NS_OK;
 }
 
+nsresult ContentPrincipal::Init(ContentPrincipal* aOther,
+                                const OriginAttributes& aOriginAttributes) {
+  NS_ENSURE_ARG(aOther);
+
+  mCodebase = aOther->mCodebase;
+  FinishInit(aOther, aOriginAttributes);
+
+  mDomain = aOther->mDomain;
+  mAddon = aOther->mAddon;
+  return NS_OK;
+}
+
 nsresult ContentPrincipal::GetScriptLocation(nsACString& aStr) {
   return mCodebase->GetSpec(aStr);
 }
 
 /* static */ nsresult ContentPrincipal::GenerateOriginNoSuffixFromURI(
     nsIURI* aURI, nsACString& aOriginNoSuffix) {
   if (!aURI) {
     return NS_ERROR_FAILURE;
--- a/caps/ContentPrincipal.h
+++ b/caps/ContentPrincipal.h
@@ -33,16 +33,18 @@ class ContentPrincipal final : public Ba
 
   ContentPrincipal();
 
   static PrincipalKind Kind() { return eCodebasePrincipal; }
 
   // Init() must be called before the principal is in a usable state.
   nsresult Init(nsIURI* aCodebase, const OriginAttributes& aOriginAttributes,
                 const nsACString& aOriginNoSuffix);
+  nsresult Init(ContentPrincipal* aOther,
+                const OriginAttributes& aOriginAttributes);
 
   virtual nsresult GetScriptLocation(nsACString& aStr) override;
 
   nsresult GetSiteIdentifier(SiteIdentifier& aSite) override;
 
   static nsresult GenerateOriginNoSuffixFromURI(nsIURI* aURI,
                                                 nsACString& aOrigin);
 
--- a/caps/nsIScriptSecurityManager.idl
+++ b/caps/nsIScriptSecurityManager.idl
@@ -150,16 +150,24 @@ interface nsIScriptSecurityManager : nsI
     /**
      * Returns a principal that has the OriginAttributes of the docshell.
      * @param docShell to get the OriginAttributes from.
      */
     nsIPrincipal getDocShellCodebasePrincipal(in nsIURI uri,
                                               in nsIDocShell docShell);
 
     /**
+     * If this is a codebase principal, return a copy with different
+     * origin attributes.
+     */
+    [implicit_jscontext]
+    nsIPrincipal principalWithOA(in nsIPrincipal principal,
+                                 in jsval originAttributes);
+
+    /**
      * Returns a principal whose origin is composed of |uri| and |originAttributes|.
      * See nsIPrincipal.idl for a description of origin attributes, and
      * ChromeUtils.webidl for a list of origin attributes and their defaults.
      */
     [implicit_jscontext]
     nsIPrincipal createCodebasePrincipal(in nsIURI uri, in jsval originAttributes);
 
     /**
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -1253,16 +1253,45 @@ nsScriptSecurityManager::GetDocShellCode
     nsIURI* aURI, nsIDocShell* aDocShell, nsIPrincipal** aPrincipal) {
   nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(
       aURI, nsDocShell::Cast(aDocShell)->GetOriginAttributes());
   prin.forget(aPrincipal);
   return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
+nsScriptSecurityManager::PrincipalWithOA(
+    nsIPrincipal* aPrincipal, JS::Handle<JS::Value> aOriginAttributes,
+    JSContext* aCx, nsIPrincipal** aReturnPrincipal) {
+  if (!aPrincipal) {
+    return NS_OK;
+  }
+  if (aPrincipal->GetIsCodebasePrincipal()) {
+    OriginAttributes attrs;
+    if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    RefPtr<ContentPrincipal> copy = new ContentPrincipal();
+    ContentPrincipal* contentPrincipal =
+        static_cast<ContentPrincipal*>(aPrincipal);
+    nsresult rv = copy->Init(contentPrincipal, attrs);
+    NS_ENSURE_SUCCESS(rv, rv);
+    copy.forget(aReturnPrincipal);
+  } else {
+    // We do this for null principals, system principals (both fine)
+    // ... and expanded principals, where we should probably do something
+    // cleverer, but I also don't think we care too much.
+    nsCOMPtr<nsIPrincipal> prin = aPrincipal;
+    prin.forget(aReturnPrincipal);
+  }
+
+  return *aReturnPrincipal ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
 nsScriptSecurityManager::CanCreateWrapper(JSContext* cx, const nsIID& aIID,
                                           nsISupports* aObj,
                                           nsIClassInfo* aClassInfo) {
   // XXX Special case for Exception ?
 
   // We give remote-XUL allowlisted domains a free pass here. See bug 932906.
   JS::Rooted<JS::Realm*> contextRealm(cx, JS::GetCurrentRealmOrNull(cx));
   MOZ_RELEASE_ASSERT(contextRealm);
--- a/dom/security/test/csp/browser.ini
+++ b/dom/security/test/csp/browser.ini
@@ -5,9 +5,12 @@ support-files =
   !/dom/security/test/csp/file_web_manifest.json
   !/dom/security/test/csp/file_web_manifest.json^headers^
   !/dom/security/test/csp/file_web_manifest_https.html
   !/dom/security/test/csp/file_web_manifest_https.json
   !/dom/security/test/csp/file_web_manifest_mixed_content.html
   !/dom/security/test/csp/file_web_manifest_remote.html
 [browser_test_web_manifest.js]
 [browser_test_web_manifest_mixed_content.js]
+[browser_test_uir_optional_clicks.js]
+support-files =
+  file_csp_meta_uir.html
 [browser_manifest-src-override-default-src.js]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/browser_test_uir_optional_clicks.js
@@ -0,0 +1,14 @@
+"use strict"
+
+const TEST_PATH_HTTP = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
+const TEST_PATH_HTTPS = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
+
+add_task(async function() {
+  await BrowserTestUtils.withNewTab(TEST_PATH_HTTPS + "file_csp_meta_uir.html", async function(browser) {
+    let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
+    BrowserTestUtils.synthesizeMouse("#mylink", 2, 2, {accelKey: true}, browser);
+    let tab = await newTabPromise;
+    is(tab.linkedBrowser.currentURI.scheme, "https", "Should have opened https page.");
+    BrowserTestUtils.removeTab(tab);
+  });
+});
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_csp_meta_uir.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Hello World</title>
+ <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+</head>
+<body>
+  <script>
+    document.write("<a href='" + document.location.href.replace(/^https/, "http") + "' id='mylink'>Click me</a>");
+  </script>
+</body>
+</html>