Bug 1300720 - Part 2: Lazily initialize nsScriptSecurityManager::mFileURIWhitelist. r=bholley draft
authorCameron McCormack <cam@mcc.id.au>
Mon, 03 Oct 2016 12:09:47 +0800
changeset 419989 a51323abb5c2a330bede0a7bb8eefe0dc323b22a
parent 419988 984f3282608d2ef105f2b077715384e96ccf0188
child 532694 dfc879436d557b5a2488a829961011957d57ca39
push id31063
push userbmo:cam@mcc.id.au
push dateMon, 03 Oct 2016 04:10:31 +0000
reviewersbholley
bugs1300720
milestone52.0a1
Bug 1300720 - Part 2: Lazily initialize nsScriptSecurityManager::mFileURIWhitelist. r=bholley MozReview-Commit-ID: GeFVryehKBf
caps/nsScriptSecurityManager.cpp
caps/nsScriptSecurityManager.h
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -918,18 +918,18 @@ nsScriptSecurityManager::CheckLoadURIFla
     // Check for target URI pointing to a file
     rv = NS_URIChainHasFlags(aTargetURI,
                              nsIProtocolHandler::URI_IS_LOCAL_FILE,
                              &hasFlags);
     NS_ENSURE_SUCCESS(rv, rv);
     if (hasFlags) {
         // Allow domains that were whitelisted in the prefs. In 99.9% of cases,
         // this array is empty.
-        for (size_t i = 0; i < mFileURIWhitelist.Length(); ++i) {
-            if (EqualOrSubdomain(aSourceURI, mFileURIWhitelist[i])) {
+        for (nsIURI* uri : EnsureFileURIWhitelist()) {
+            if (EqualOrSubdomain(aSourceURI, uri)) {
                 return NS_OK;
             }
         }
 
         // Allow chrome://
         bool isChrome = false;
         if (NS_SUCCEEDED(aSourceBaseURI->SchemeIs("chrome", &isChrome)) && isChrome) {
             return NS_OK;
@@ -1454,48 +1454,17 @@ uint32_t SkipUntil(const nsCString& str,
 inline void
 nsScriptSecurityManager::ScriptSecurityPrefChanged()
 {
     MOZ_ASSERT(mPrefInitialized);
     mIsJavaScriptEnabled =
         Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
     sStrictFileOriginPolicy =
         Preferences::GetBool(sFileOriginPolicyPrefName, false);
-
-    //
-    // Rebuild the set of principals for which we allow file:// URI loads. This
-    // implements a small subset of an old pref-based CAPS people that people
-    // have come to depend on. See bug 995943.
-    //
-
-    mFileURIWhitelist.Clear();
-    auto policies = mozilla::Preferences::GetCString("capability.policy.policynames");
-    for (uint32_t base = SkipPast<IsWhitespaceOrComma>(policies, 0), bound = 0;
-         base < policies.Length();
-         base = SkipPast<IsWhitespaceOrComma>(policies, bound))
-    {
-        // Grab the current policy name.
-        bound = SkipUntil<IsWhitespaceOrComma>(policies, base);
-        auto policyName = Substring(policies, base, bound - base);
-
-        // Figure out if this policy allows loading file:// URIs. If not, we can skip it.
-        nsCString checkLoadURIPrefName = NS_LITERAL_CSTRING("capability.policy.") +
-                                         policyName +
-                                         NS_LITERAL_CSTRING(".checkloaduri.enabled");
-        if (!Preferences::GetString(checkLoadURIPrefName.get()).LowerCaseEqualsLiteral("allaccess")) {
-            continue;
-        }
-
-        // Grab the list of domains associated with this policy.
-        nsCString domainPrefName = NS_LITERAL_CSTRING("capability.policy.") +
-                                   policyName +
-                                   NS_LITERAL_CSTRING(".sites");
-        auto siteList = Preferences::GetCString(domainPrefName.get());
-        AddSitesToFileURIWhitelist(siteList);
-    }
+    mFileURIWhitelist.reset();
 }
 
 void
 nsScriptSecurityManager::AddSitesToFileURIWhitelist(const nsCString& aSiteList)
 {
     for (uint32_t base = SkipPast<IsWhitespace>(aSiteList, 0), bound = 0;
          base < aSiteList.Length();
          base = SkipPast<IsWhitespace>(aSiteList, bound))
@@ -1511,17 +1480,17 @@ nsScriptSecurityManager::AddSitesToFileU
             AddSitesToFileURIWhitelist(NS_LITERAL_CSTRING("https://") + site);
             continue;
         }
 
         // Convert it to a URI and add it to our list.
         nsCOMPtr<nsIURI> uri;
         nsresult rv = NS_NewURI(getter_AddRefs(uri), site, nullptr, nullptr, sIOService);
         if (NS_SUCCEEDED(rv)) {
-            mFileURIWhitelist.AppendElement(uri);
+            mFileURIWhitelist.ref().AppendElement(uri);
         } else {
             nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
             if (console) {
                 nsAutoString msg = NS_LITERAL_STRING("Unable to to add site to file:// URI whitelist: ") +
                                    NS_ConvertASCIItoUTF16(site);
                 console->LogStringMessage(msg.get());
             }
         }
@@ -1671,8 +1640,50 @@ nsScriptSecurityManager::PolicyAllowsScr
     rv = superExceptions->ContainsSuperDomain(aURI, &contains);
     NS_ENSURE_SUCCESS(rv, rv);
     if (contains) {
         *aRv = !*aRv;
     }
 
     return NS_OK;
 }
+
+const nsTArray<nsCOMPtr<nsIURI>>&
+nsScriptSecurityManager::EnsureFileURIWhitelist()
+{
+    if (mFileURIWhitelist.isSome()) {
+        return mFileURIWhitelist.ref();
+    }
+
+    //
+    // Rebuild the set of principals for which we allow file:// URI loads. This
+    // implements a small subset of an old pref-based CAPS people that people
+    // have come to depend on. See bug 995943.
+    //
+
+    mFileURIWhitelist.emplace();
+    auto policies = mozilla::Preferences::GetCString("capability.policy.policynames");
+    for (uint32_t base = SkipPast<IsWhitespaceOrComma>(policies, 0), bound = 0;
+         base < policies.Length();
+         base = SkipPast<IsWhitespaceOrComma>(policies, bound))
+    {
+        // Grab the current policy name.
+        bound = SkipUntil<IsWhitespaceOrComma>(policies, base);
+        auto policyName = Substring(policies, base, bound - base);
+
+        // Figure out if this policy allows loading file:// URIs. If not, we can skip it.
+        nsCString checkLoadURIPrefName = NS_LITERAL_CSTRING("capability.policy.") +
+                                         policyName +
+                                         NS_LITERAL_CSTRING(".checkloaduri.enabled");
+        if (!Preferences::GetString(checkLoadURIPrefName.get()).LowerCaseEqualsLiteral("allaccess")) {
+            continue;
+        }
+
+        // Grab the list of domains associated with this policy.
+        nsCString domainPrefName = NS_LITERAL_CSTRING("capability.policy.") +
+                                   policyName +
+                                   NS_LITERAL_CSTRING(".sites");
+        auto siteList = Preferences::GetCString(domainPrefName.get());
+        AddSitesToFileURIWhitelist(siteList);
+    }
+
+    return mFileURIWhitelist.ref();
+}
--- a/caps/nsScriptSecurityManager.h
+++ b/caps/nsScriptSecurityManager.h
@@ -119,20 +119,28 @@ private:
     nsresult GetChannelResultPrincipal(nsIChannel* aChannel,
                                        nsIPrincipal** aPrincipal,
                                        bool aIgnoreSandboxing);
 
     nsresult
     CheckLoadURIFlags(nsIURI* aSourceURI, nsIURI* aTargetURI, nsIURI* aSourceBaseURI,
                       nsIURI* aTargetBaseURI, uint32_t aFlags);
 
+    // Returns the file URI whitelist, initializing it if it has not been
+    // initialized.
+    const nsTArray<nsCOMPtr<nsIURI>>& EnsureFileURIWhitelist();
+
     nsCOMPtr<nsIPrincipal> mSystemPrincipal;
     bool mPrefInitialized;
     bool mIsJavaScriptEnabled;
-    nsTArray<nsCOMPtr<nsIURI>> mFileURIWhitelist;
+
+    // List of URIs whose domains and sub-domains are whitelisted to allow
+    // access to file: URIs.  Lazily initialized; isNothing() when not yet
+    // initialized.
+    mozilla::Maybe<nsTArray<nsCOMPtr<nsIURI>>> mFileURIWhitelist;
 
     // This machinery controls new-style domain policies. The old-style
     // policy machinery will be removed soon.
     nsCOMPtr<nsIDomainPolicy> mDomainPolicy;
 
     // Cached addon policy service. We can't generate this in Init() because
     // that's too early to get a service.
     mozilla::Maybe<nsCOMPtr<nsIAddonPolicyService>> mAddonPolicyService;