summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--caps/nsScriptSecurityManager.cpp19
-rw-r--r--dom/base/Location.cpp20
-rw-r--r--dom/security/nsCSPService.cpp5
-rw-r--r--dom/security/test/csp/file_nonce_redirector.sjs25
-rw-r--r--dom/security/test/csp/file_nonce_redirects.html23
-rw-r--r--dom/security/test/csp/mochitest.ini3
-rw-r--r--dom/security/test/csp/test_nonce_redirects.html47
-rw-r--r--netwerk/base/LoadInfo.cpp23
-rw-r--r--netwerk/base/nsILoadInfo.idl17
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;