diff options
-rw-r--r-- | caps/nsScriptSecurityManager.cpp | 19 | ||||
-rw-r--r-- | dom/base/Location.cpp | 20 | ||||
-rw-r--r-- | dom/security/nsCSPService.cpp | 5 | ||||
-rw-r--r-- | dom/security/test/csp/file_nonce_redirector.sjs | 25 | ||||
-rw-r--r-- | dom/security/test/csp/file_nonce_redirects.html | 23 | ||||
-rw-r--r-- | dom/security/test/csp/mochitest.ini | 3 | ||||
-rw-r--r-- | dom/security/test/csp/test_nonce_redirects.html | 47 | ||||
-rw-r--r-- | netwerk/base/LoadInfo.cpp | 23 | ||||
-rw-r--r-- | netwerk/base/nsILoadInfo.idl | 17 |
9 files changed, 175 insertions, 7 deletions
diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index a219dcaed..0277f771d 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -836,6 +836,16 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, // exception for foo: linking to view-source:foo for reftests... return NS_OK; } + else if ((!sourceScheme.EqualsIgnoreCase("http") && + !sourceScheme.EqualsIgnoreCase("https")) && + targetScheme.EqualsIgnoreCase("moz-icon")) + { + // Exception for linking to moz-icon://.ext?size=... + // Note that because targetScheme is the base (innermost) URI scheme, + // this does NOT allow e.g. file -> moz-icon:file:///... links. + // This is intentional. + return NS_OK; + } // If we get here, check all the schemes can link to each other, from the top down: nsCaseInsensitiveCStringComparator stringComparator; @@ -976,9 +986,12 @@ nsScriptSecurityManager::CheckLoadURIFlags(nsIURI *aSourceURI, if (hasFlags) { if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) { - // For now, don't change behavior for resource:// or moz-icon:// and - // just allow them. - if (!targetScheme.EqualsLiteral("chrome")) { + // For now, don't change behavior for resource:// and + // just allow it. This is required for extensions injecting + // extension-internal resource URLs in snippets in pages, e.g. + // Adding custom controls in-page. + if (!targetScheme.EqualsLiteral("chrome") && + !targetScheme.EqualsLiteral("moz-icon")) { return NS_OK; } diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp index b6b95aaa6..7b3722f09 100644 --- a/dom/base/Location.cpp +++ b/dom/base/Location.cpp @@ -393,6 +393,10 @@ Location::GetHost(nsAString& aHost) NS_IMETHODIMP Location::SetHost(const nsAString& aHost) { + if (aHost.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsCOMPtr<nsIURI> uri; nsresult rv = GetWritableURI(getter_AddRefs(uri)); if (NS_WARN_IF(NS_FAILED(rv) || !uri)) { @@ -424,6 +428,10 @@ Location::GetHostname(nsAString& aHostname) NS_IMETHODIMP Location::SetHostname(const nsAString& aHostname) { + if (aHostname.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsCOMPtr<nsIURI> uri; nsresult rv = GetWritableURI(getter_AddRefs(uri)); if (NS_WARN_IF(NS_FAILED(rv) || !uri)) { @@ -507,6 +515,10 @@ nsresult Location::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase, bool aReplace) { + if (aHref.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsresult result; nsCOMPtr<nsIURI> newUri; @@ -694,6 +706,10 @@ Location::GetProtocol(nsAString& aProtocol) NS_IMETHODIMP Location::SetProtocol(const nsAString& aProtocol) { + if (aProtocol.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsCOMPtr<nsIURI> uri; nsresult rv = GetWritableURI(getter_AddRefs(uri)); if (NS_WARN_IF(NS_FAILED(rv) || !uri)) { @@ -747,6 +763,10 @@ Location::GetSearch(nsAString& aSearch) NS_IMETHODIMP Location::SetSearch(const nsAString& aSearch) { + if (aSearch.IsEmpty()) { + return NS_OK; // Ignore empty string + } + nsresult rv = SetSearchInternal(aSearch); if (NS_FAILED(rv)) { return rv; diff --git a/dom/security/nsCSPService.cpp b/dom/security/nsCSPService.cpp index 7344e19fa..4807c9aa4 100644 --- a/dom/security/nsCSPService.cpp +++ b/dom/security/nsCSPService.cpp @@ -288,6 +288,7 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(policyType); int16_t aDecision = nsIContentPolicy::ACCEPT; + nsCOMPtr<nsISupports> requestContext = loadInfo->GetLoadingContext(); // 1) Apply speculative CSP for preloads if (isPreload) { nsCOMPtr<nsIContentSecurityPolicy> preloadCsp; @@ -298,7 +299,7 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel, preloadCsp->ShouldLoad(policyType, // load type per nsIContentPolicy (uint32_t) newUri, // nsIURI nullptr, // nsIURI - nullptr, // nsISupports + requestContext, // nsISupports EmptyCString(), // ACString - MIME guess originalUri, // aExtra &aDecision); @@ -321,7 +322,7 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel, csp->ShouldLoad(policyType, // load type per nsIContentPolicy (uint32_t) newUri, // nsIURI nullptr, // nsIURI - nullptr, // nsISupports + requestContext, // nsISupports EmptyCString(), // ACString - MIME guess originalUri, // aExtra &aDecision); diff --git a/dom/security/test/csp/file_nonce_redirector.sjs b/dom/security/test/csp/file_nonce_redirector.sjs new file mode 100644 index 000000000..21a8f4e9c --- /dev/null +++ b/dom/security/test/csp/file_nonce_redirector.sjs @@ -0,0 +1,25 @@ +// custom *.sjs file for +// Bug 1469150:Scripts with valid nonce get blocked if URL redirects. + +const URL_PATH = "example.com/tests/dom/security/test/csp/"; + +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache", false); + let queryStr = request.queryString; + + if (queryStr === "redirect") { + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", + "https://" + URL_PATH + "file_nonce_redirector.sjs?load", false); + return; + } + + if (queryStr === "load") { + response.setHeader("Content-Type", "application/javascript", false); + response.write("console.log('script loaded');"); + return; + } + + // we should never get here - return something unexpected + response.write("d'oh"); +} diff --git a/dom/security/test/csp/file_nonce_redirects.html b/dom/security/test/csp/file_nonce_redirects.html new file mode 100644 index 000000000..e29116490 --- /dev/null +++ b/dom/security/test/csp/file_nonce_redirects.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html> + <head> + <meta charset='utf-8'> + <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-abcd1234'"> + <title>Bug 1469150:Scripts with valid nonce get blocked if URL redirects</title> + </head> +<body> + +<script nonce='abcd1234' id='redirectScript'></script> + +<script nonce='abcd1234' type='application/javascript'> + var redirectScript = document.getElementById('redirectScript'); + redirectScript.onload = function(e) { + window.parent.postMessage({result: 'script-loaded'}, '*'); + }; + redirectScript.onerror = function(e) { + window.parent.postMessage({result: 'script-blocked'}, '*'); + } + redirectScript.src = 'file_nonce_redirector.sjs?redirect'; +</script> +</body> +</html> diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini index 33b112020..86b7fd0cd 100644 --- a/dom/security/test/csp/mochitest.ini +++ b/dom/security/test/csp/mochitest.ini @@ -88,6 +88,8 @@ support-files = file_shouldprocess.html file_nonce_source.html file_nonce_source.html^headers^ + file_nonce_redirects.html + file_nonce_redirector.sjs file_bug941404.html file_bug941404_xhr.html file_bug941404_xhr.html^headers^ @@ -245,6 +247,7 @@ skip-if = toolkit == 'android' # Times out, not sure why (bug 1008445) [test_frame_ancestors_ro.html] [test_policyuri_regression_from_multipolicy.html] [test_nonce_source.html] +[test_nonce_redirects.html] [test_bug941404.html] [test_form-action.html] [test_hash_source.html] diff --git a/dom/security/test/csp/test_nonce_redirects.html b/dom/security/test/csp/test_nonce_redirects.html new file mode 100644 index 000000000..f84fdcc7b --- /dev/null +++ b/dom/security/test/csp/test_nonce_redirects.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1469150:Scripts with valid nonce get blocked if URL redirects</title> + <!-- Including SimpleTest.js so we can use waitForExplicitFinish !--> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<iframe style="width:100%;" id="testframe"></iframe> + +<script class="testbody" type="text/javascript"> + +/* Description of the test: + * We load a script with a matching nonce, which redirects + * and we make sure that script is allowed. + */ + +SimpleTest.waitForExplicitFinish(); + +function finishTest() { + window.removeEventListener("message", receiveMessage); + SimpleTest.finish(); +} + +function checkResults(aResult) { + + if (aResult === "script-loaded") { + ok(true, "expected result: script loaded"); + } + else { + ok(false, "unexpected result: script blocked"); + } + finishTest(); +} + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + checkResults(event.data.result); +} + +document.getElementById("testframe").src = "file_nonce_redirects.html"; + +</script> +</body> +</html> diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp index a8c9a5a25..ebe9d4703 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -81,7 +81,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, // This constructor shouldn't be used for TYPE_DOCUMENT loads that don't // have a loadingPrincipal - MOZ_ASSERT(skipContentTypeCheck || + MOZ_ASSERT(skipContentTypeCheck || mLoadingPrincipal || mInternalContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT); // TODO(bug 1259873): Above, we initialize mIsThirdPartyContext to false meaning @@ -493,6 +493,27 @@ LoadInfo::ContextForTopLevelLoad() return context; } +already_AddRefed<nsISupports> +LoadInfo::GetLoadingContext() +{ + nsCOMPtr<nsISupports> context; + if (mInternalContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) { + context = ContextForTopLevelLoad(); + } + else { + context = LoadingNode(); + } + return context.forget(); +} + +NS_IMETHODIMP +LoadInfo::GetLoadingContextXPCOM(nsISupports** aResult) +{ + nsCOMPtr<nsISupports> context = GetLoadingContext(); + context.forget(aResult); + return NS_OK; +} + NS_IMETHODIMP LoadInfo::GetSecurityFlags(nsSecurityFlags* aResult) { diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl index 9a883ff98..bc609c317 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -10,7 +10,7 @@ interface nsIDOMDocument; interface nsINode; interface nsIPrincipal; - +native LoadContextRef(already_AddRefed<nsISupports>); %{C++ #include "nsTArray.h" #include "mozilla/BasePrincipal.h" @@ -334,6 +334,21 @@ interface nsILoadInfo : nsISupports nsISupports binaryContextForTopLevelLoad(); /** + * For all loads except loads of TYPE_DOCUMENT, the loadingContext + * simply returns the loadingNode. For loads of TYPE_DOCUMENT this + * will return the context available for top-level loads which + * do not have a loadingNode. + */ + [binaryname(LoadingContextXPCOM)] + readonly attribute nsISupports loadingContext; + + /** + * A C++ friendly version of the loadingContext. + */ + [noscript, notxpcom, nostdcall, binaryname(GetLoadingContext)] + LoadContextRef binaryGetLoadingContext(); + + /** * The securityFlags of that channel. */ readonly attribute nsSecurityFlags securityFlags; |