Bug 1309310, r=bz a=gchang
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Fri, 11 Nov 2016 13:09:49 +0000
changeset 452571 61d9cd6239b2a1eb6ec3fd16f0a4ffe57c6ecf73
parent 452570 9c5f97295bb524035ee20aed657c9001ccc6ca59
child 452572 ae5275b8c53ba76cb98576e4e2a3031b0d659ba3
push id39418
push userbmo:twointofive@gmail.com
push dateWed, 21 Dec 2016 20:59:30 +0000
reviewersbz, gchang
bugs1309310
milestone51.0
Bug 1309310, r=bz a=gchang MozReview-Commit-ID: KLaMv6zfxR8
browser/base/content/test/general/browser_bug575561.js
caps/nsScriptSecurityManager.cpp
caps/tests/mochitest/browser_checkloaduri.js
netwerk/base/nsIProtocolHandler.idl
netwerk/protocol/about/nsAboutProtocolHandler.cpp
--- a/browser/base/content/test/general/browser_bug575561.js
+++ b/browser/base/content/test/general/browser_bug575561.js
@@ -32,21 +32,21 @@ add_task(function*() {
   // Tests link to http://www.example.com/browser/browser/base/content/test/general/dummy_page.html
   yield testLink(4, true, false);
 
   // Pinned: Link to a data: URI should not open a new tab
   // Tests link to data:text/html,<!DOCTYPE html><html><body>Another Page</body></html>
   yield testLink(5, true, false);
 
   // Pinned: Link to an about: URI should not open a new tab
-  // Tests link to about:mozilla
+  // Tests link to about:logo
   yield testLink(function(doc) {
     let link = doc.createElement("a");
     link.textContent = "Link to Mozilla";
-    link.href = "about:mozilla";
+    link.href = "about:logo";
     doc.body.appendChild(link);
     return link;
   }, true, false, false, "about:robots");
 });
 
 var waitForPageLoad = Task.async(function*(browser, linkLocation) {
   yield waitForDocLoadComplete();
 
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -785,25 +785,48 @@ nsScriptSecurityManager::CheckLoadURIWit
         // exception for foo: linking to view-source:foo for reftests...
         return NS_OK;
     }
 
     // If we get here, check all the schemes can link to each other, from the top down:
     nsCaseInsensitiveCStringComparator stringComparator;
     nsCOMPtr<nsIURI> currentURI = sourceURI;
     nsCOMPtr<nsIURI> currentOtherURI = aTargetURI;
+
+    bool denySameSchemeLinks = false;
+    rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_SCHEME_NOT_SELF_LINKABLE,
+                             &denySameSchemeLinks);
+    if (NS_FAILED(rv)) return rv;
+
     while (currentURI && currentOtherURI) {
         nsAutoCString scheme, otherScheme;
         currentURI->GetScheme(scheme);
         currentOtherURI->GetScheme(otherScheme);
 
-        // If schemes are not equal, check if the URI flags of the current
-        // target URI allow the current source URI to link to it.
+        bool schemesMatch = scheme.Equals(otherScheme, stringComparator);
+        bool isSamePage;
+        // about: URIs are special snowflakes.
+        if (scheme.EqualsLiteral("about")) {
+            nsAutoCString module, otherModule;
+            isSamePage = schemesMatch &&
+                NS_SUCCEEDED(NS_GetAboutModuleName(currentURI, module)) &&
+                NS_SUCCEEDED(NS_GetAboutModuleName(currentOtherURI, otherModule)) &&
+                module.Equals(otherModule);
+        } else {
+            bool equalExceptRef = false;
+            rv = currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef);
+            isSamePage = NS_SUCCEEDED(rv) && equalExceptRef;
+        }
+
+        // If schemes are not equal, or they're equal but the target URI
+        // is different from the source URI and doesn't always allow linking
+        // from the same scheme, check if the URI flags of the current target
+        // URI allow the current source URI to link to it.
         // The policy is specified by the protocol flags on both URIs.
-        if (!scheme.Equals(otherScheme, stringComparator)) {
+        if (!schemesMatch || (denySameSchemeLinks && !isSamePage)) {
             return CheckLoadURIFlags(currentURI, currentOtherURI,
                                      sourceBaseURI, targetBaseURI, aFlags);
         }
         // Otherwise... check if we can nest another level:
         nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI);
         nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(currentOtherURI);
 
         // If schemes match and neither URI is nested further, we're OK.
--- a/caps/tests/mochitest/browser_checkloaduri.js
+++ b/caps/tests/mochitest/browser_checkloaduri.js
@@ -47,16 +47,28 @@ const URLs = new Map([
     ["view-source:http://www.example2.com", true, true, true],
     ["view-source:https://www.example2.com", true, true, true],
     ["view-source:feed:http://www.example2.com", false, false, true],
     ["feed:view-source:http://www.example2.com", false, false, false],
     ["data:text/html,Hi", true, false, true],
     ["view-source:data:text/html,Hi", true, false, true],
     ["javascript:alert('hi')", true, false, true],
   ]],
+  ["about:foo", [
+    ["about:foo?", true, true, true],
+    ["about:foo?bar", true, true, true],
+    ["about:foo#", true, true, true],
+    ["about:foo#bar", true, true, true],
+    ["about:foo?#", true, true, true],
+    ["about:foo?bar#baz", true, true, true],
+    ["about:bar", false, false, true],
+    ["about:bar?foo#baz", false, false, true],
+    ["about:bar?foo", false, false, true],
+    ["http://www.example.com/", true, true, true],
+  ]],
 ]);
 
 function testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, flags) {
   let threw = false;
   let targetURI;
   try {
     targetURI = makeURI(target);
   } catch (ex) {
--- a/netwerk/base/nsIProtocolHandler.idl
+++ b/netwerk/base/nsIProtocolHandler.idl
@@ -295,16 +295,22 @@ interface nsIProtocolHandler : nsISuppor
      */
     const unsigned long URI_FETCHABLE_BY_ANYONE = (1 << 19);
 
     /**
      * If this flag is set, then the origin for this protocol is the full URI
      * spec, not just the scheme + host + port.
      */
     const unsigned long ORIGIN_IS_FULL_SPEC = (1 << 20);
+
+    /**
+     * If this flag is set, the URI does not always allow content using the same
+     * protocol to link to it.
+     */
+    const unsigned long URI_SCHEME_NOT_SELF_LINKABLE = (1 << 21);
 };
 
 %{C++
 /**
  * Protocol handlers are registered with XPCOM under the following CONTRACTID prefix:
  */
 #define NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "@mozilla.org/network/protocol;1?name="
 /**
--- a/netwerk/protocol/about/nsAboutProtocolHandler.cpp
+++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp
@@ -62,17 +62,17 @@ nsAboutProtocolHandler::GetDefaultPort(i
 {
     *result = -1;        // no port for about: URLs
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAboutProtocolHandler::GetProtocolFlags(uint32_t *result)
 {
-    *result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD;
+    *result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | URI_SCHEME_NOT_SELF_LINKABLE;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAboutProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags)
 {
     // First use the default (which is "unsafe for content"):
     GetProtocolFlags(aFlags);