diff options
Diffstat (limited to 'dom/security')
53 files changed, 920 insertions, 1006 deletions
diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index 5e435d4ca..65be02809 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -171,9 +171,10 @@ nsCSPContext::ShouldLoad(nsContentPolicyType aContentType, } } - // aExtra is only non-null if the channel got redirected. - bool wasRedirected = (aExtra != nullptr); + // aExtra holds the original URI of the channel if the + // channel got redirected (until we fix Bug 1332422). nsCOMPtr<nsIURI> originalURI = do_QueryInterface(aExtra); + bool wasRedirected = originalURI; bool permitted = permitsInternal(dir, aContentLocation, @@ -219,14 +220,6 @@ nsCSPContext::permitsInternal(CSPDirective aDir, nsAutoString violatedDirective; for (uint32_t p = 0; p < mPolicies.Length(); p++) { - - // According to the W3C CSP spec, frame-ancestors checks are ignored for - // report-only policies (when "monitoring"). - if (aDir == nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE && - mPolicies[p]->getReportOnlyFlag()) { - continue; - } - if (!mPolicies[p]->permits(aDir, aContentLocation, aNonce, @@ -824,15 +817,6 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource, { NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1); -#ifdef MOZ_B2G - // load group information (on process-split necko implementations like b2g). - // (fix this in bug 1011086) - if (!mCallingChannelLoadGroup) { - NS_WARNING("Load group required but not present for report sending; cannot send CSP violation reports"); - return NS_ERROR_FAILURE; - } -#endif - dom::CSPReport report; nsresult 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/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp index 49832f8f4..71c8e3433 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -258,6 +258,9 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType) case nsIContentPolicy::TYPE_CSP_REPORT: return nsIContentSecurityPolicy::NO_DIRECTIVE; + case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: + return nsIContentSecurityPolicy::NO_DIRECTIVE; + // Fall through to error for all other directives default: MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective"); diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp index c4e1ed8e1..570730312 100644 --- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -1,20 +1,97 @@ #include "nsContentSecurityManager.h" +#include "nsEscape.h" #include "nsIChannel.h" #include "nsIHttpChannelInternal.h" #include "nsIStreamListener.h" #include "nsILoadInfo.h" +#include "nsIOService.h" #include "nsContentUtils.h" #include "nsCORSListenerProxy.h" #include "nsIStreamListener.h" -#include "nsIDocument.h" -#include "nsMixedContentBlocker.h" +#include "nsCDefaultURIFixup.h" +#include "nsIURIFixup.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/TabChild.h" NS_IMPL_ISUPPORTS(nsContentSecurityManager, nsIContentSecurityManager, nsIChannelEventSink) +/* static */ bool +nsContentSecurityManager::AllowTopLevelNavigationToDataURI(nsIChannel* aChannel) +{ + // Let's block all toplevel document navigations to a data: URI. + // In all cases where the toplevel document is navigated to a + // data: URI the triggeringPrincipal is a codeBasePrincipal, or + // a NullPrincipal. In other cases, e.g. typing a data: URL into + // the URL-Bar, the triggeringPrincipal is a SystemPrincipal; + // we don't want to block those loads. Only exception, loads coming + // from an external applicaton (e.g. Thunderbird) don't load + // using a codeBasePrincipal, but we want to block those loads. + if (!mozilla::net::nsIOService::BlockToplevelDataUriNavigations()) { + return true; + } + nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); + if (!loadInfo) { + return true; + } + if (loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) { + return true; + } + if (loadInfo->GetForceAllowDataURI()) { + // if the loadinfo explicitly allows the data URI navigation, let's allow it now + return true; + } + nsCOMPtr<nsIURI> uri; + nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, true); + bool isDataURI = + (NS_SUCCEEDED(uri->SchemeIs("data", &isDataURI)) && isDataURI); + if (!isDataURI) { + return true; + } + // Whitelist data: images as long as they are not SVGs + nsAutoCString filePath; + uri->GetFilePath(filePath); + if (StringBeginsWith(filePath, NS_LITERAL_CSTRING("image/")) && + !StringBeginsWith(filePath, NS_LITERAL_CSTRING("image/svg+xml"))) { + return true; + } + // Whitelist data: PDFs and JSON + if (StringBeginsWith(filePath, NS_LITERAL_CSTRING("application/pdf")) || + StringBeginsWith(filePath, NS_LITERAL_CSTRING("application/json"))) { + return true; + } + // Redirecting to a toplevel data: URI is not allowed, hence we make + // sure the RedirectChain is empty. + if (!loadInfo->GetLoadTriggeredFromExternal() && + nsContentUtils::IsSystemPrincipal(loadInfo->TriggeringPrincipal()) && + loadInfo->RedirectChain().IsEmpty()) { + return true; + } + nsAutoCString dataSpec; + uri->GetSpec(dataSpec); + if (dataSpec.Length() > 50) { + dataSpec.Truncate(50); + dataSpec.AppendLiteral("..."); + } + nsCOMPtr<nsITabChild> tabChild = do_QueryInterface(loadInfo->ContextForTopLevelLoad()); + nsCOMPtr<nsIDocument> doc; + if (tabChild) { + doc = static_cast<mozilla::dom::TabChild*>(tabChild.get())->GetDocument(); + } + NS_ConvertUTF8toUTF16 specUTF16(NS_UnescapeURL(dataSpec)); + const char16_t* params[] = { specUTF16.get() }; + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("DATA_URI_BLOCKED"), + doc, + nsContentUtils::eSECURITY_PROPERTIES, + "BlockTopLevelDataURINavigation", + params, ArrayLength(params)); + return false; +} + static nsresult ValidateSecurityFlags(nsILoadInfo* aLoadInfo) { @@ -167,23 +244,31 @@ DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo, static nsresult DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) { - nsCOMPtr<nsIURI> uri; - nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); - NS_ENSURE_SUCCESS(rv, rv); - nsContentPolicyType contentPolicyType = aLoadInfo->GetExternalContentPolicyType(); nsContentPolicyType internalContentPolicyType = aLoadInfo->InternalContentPolicyType(); nsCString mimeTypeGuess; - nsCOMPtr<nsINode> requestingContext = nullptr; + nsCOMPtr<nsISupports> requestingContext = nullptr; -#ifdef DEBUG - // Don't enforce TYPE_DOCUMENT assertions for loads - // initiated by javascript tests. - bool skipContentTypeCheck = false; - skipContentTypeCheck = Preferences::GetBool("network.loadinfo.skip_type_assertion"); -#endif + nsCOMPtr<nsIURI> uri; + nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + + if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT || + contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) { + // TYPE_DOCUMENT and TYPE_SUBDOCUMENT loads might potentially + // be wyciwyg:// channels. Let's fix up the URI so we can + // perform proper security checks. + nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv) && urifixup) { + nsCOMPtr<nsIURI> fixedURI; + rv = urifixup->CreateExposableURI(uri, getter_AddRefs(fixedURI)); + if (NS_SUCCEEDED(rv)) { + uri = fixedURI; + } + } + } switch(contentPolicyType) { case nsIContentPolicy::TYPE_OTHER: { @@ -217,16 +302,14 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) } case nsIContentPolicy::TYPE_DOCUMENT: { - MOZ_ASSERT(skipContentTypeCheck || false, "contentPolicyType not supported yet"); + mimeTypeGuess = EmptyCString(); + requestingContext = aLoadInfo->ContextForTopLevelLoad(); break; } case nsIContentPolicy::TYPE_SUBDOCUMENT: { mimeTypeGuess = NS_LITERAL_CSTRING("text/html"); requestingContext = aLoadInfo->LoadingNode(); - MOZ_ASSERT(!requestingContext || - requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, - "type_subdocument requires requestingContext of type Document"); break; } @@ -250,10 +333,13 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) case nsIContentPolicy::TYPE_XMLHTTPREQUEST: { // alias nsIContentPolicy::TYPE_DATAREQUEST: requestingContext = aLoadInfo->LoadingNode(); - MOZ_ASSERT(!requestingContext || - requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, - "type_xml requires requestingContext of type Document"); - +#ifdef DEBUG + { + nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext); + MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::DOCUMENT_NODE, + "type_xml requires requestingContext of type Document"); + } +#endif // We're checking for the external TYPE_XMLHTTPREQUEST here in case // an addon creates a request with that type. if (internalContentPolicyType == @@ -274,18 +360,26 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); - MOZ_ASSERT(!requestingContext || - requestingContext->NodeType() == nsIDOMNode::ELEMENT_NODE, - "type_subrequest requires requestingContext of type Element"); +#ifdef DEBUG + { + nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext); + MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::ELEMENT_NODE, + "type_subrequest requires requestingContext of type Element"); + } +#endif break; } case nsIContentPolicy::TYPE_DTD: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); - MOZ_ASSERT(!requestingContext || - requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, - "type_dtd requires requestingContext of type Document"); +#ifdef DEBUG + { + nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext); + MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::DOCUMENT_NODE, + "type_dtd requires requestingContext of type Document"); + } +#endif break; } @@ -303,9 +397,13 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) mimeTypeGuess = EmptyCString(); } requestingContext = aLoadInfo->LoadingNode(); - MOZ_ASSERT(!requestingContext || - requestingContext->NodeType() == nsIDOMNode::ELEMENT_NODE, - "type_media requires requestingContext of type Element"); +#ifdef DEBUG + { + nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext); + MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::ELEMENT_NODE, + "type_media requires requestingContext of type Element"); + } +#endif break; } @@ -332,18 +430,26 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) case nsIContentPolicy::TYPE_XSLT: { mimeTypeGuess = NS_LITERAL_CSTRING("application/xml"); requestingContext = aLoadInfo->LoadingNode(); - MOZ_ASSERT(!requestingContext || - requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, - "type_xslt requires requestingContext of type Document"); +#ifdef DEBUG + { + nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext); + MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::DOCUMENT_NODE, + "type_xslt requires requestingContext of type Document"); + } +#endif break; } case nsIContentPolicy::TYPE_BEACON: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); - MOZ_ASSERT(!requestingContext || - requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, - "type_beacon requires requestingContext of type Document"); +#ifdef DEBUG + { + nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext); + MOZ_ASSERT(!node || node->NodeType() == nsIDOMNode::DOCUMENT_NODE, + "type_beacon requires requestingContext of type Document"); + } +#endif break; } @@ -365,31 +471,44 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) break; } + case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: { + mimeTypeGuess = EmptyCString(); + requestingContext = aLoadInfo->LoadingNode(); + break; + } + default: // nsIContentPolicy::TYPE_INVALID MOZ_ASSERT(false, "can not perform security check without a valid contentType"); } + // For document loads we use the triggeringPrincipal as the originPrincipal. + // Note the the loadingPrincipal for loads of TYPE_DOCUMENT is a nullptr. + nsCOMPtr<nsIPrincipal> principal = + (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT || + contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) + ? aLoadInfo->TriggeringPrincipal() + : aLoadInfo->LoadingPrincipal(); + int16_t shouldLoad = nsIContentPolicy::ACCEPT; rv = NS_CheckContentLoadPolicy(internalContentPolicyType, uri, - aLoadInfo->LoadingPrincipal(), + principal, requestingContext, mimeTypeGuess, nullptr, //extra, &shouldLoad, nsContentUtils::GetContentPolicy(), nsContentUtils::GetSecurityManager()); - NS_ENSURE_SUCCESS(rv, rv); - if (NS_CP_REJECTED(shouldLoad)) { - return NS_ERROR_CONTENT_BLOCKED; - } - if (nsMixedContentBlocker::sSendHSTSPriming) { - rv = nsMixedContentBlocker::MarkLoadInfoForPriming(uri, - requestingContext, - aLoadInfo); - return rv; + if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { + if ((NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) && + (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT || + contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT)) { + // for docshell loads we might have to return SHOW_ALT. + return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT; + } + return NS_ERROR_CONTENT_BLOCKED; } return NS_OK; @@ -529,6 +648,24 @@ nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); + nsContentPolicyType contentPolicyType = + loadInfo->GetExternalContentPolicyType(); + + if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT || + contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) { + // TYPE_DOCUMENT and TYPE_SUBDOCUMENT loads might potentially + // be wyciwyg:// channels. Let's fix up the URI so we can + // perform proper security checks. + nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv) && urifixup) { + nsCOMPtr<nsIURI> fixedURI; + rv = urifixup->CreateExposableURI(uri, getter_AddRefs(fixedURI)); + if (NS_SUCCEEDED(rv)) { + uri = fixedURI; + } + } + } + // Handle cookie policies uint32_t cookiePolicy = loadInfo->GetCookiePolicy(); if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) { diff --git a/dom/security/nsContentSecurityManager.h b/dom/security/nsContentSecurityManager.h index 912c0e89f..bab847743 100644 --- a/dom/security/nsContentSecurityManager.h +++ b/dom/security/nsContentSecurityManager.h @@ -32,6 +32,8 @@ public: static nsresult doContentSecurityCheck(nsIChannel* aChannel, nsCOMPtr<nsIStreamListener>& aInAndOutListener); + static bool AllowTopLevelNavigationToDataURI(nsIChannel* aChannel); + private: static nsresult CheckChannel(nsIChannel* aChannel); diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp index 4e80dce3f..c03628da0 100644 --- a/dom/security/nsMixedContentBlocker.cpp +++ b/dom/security/nsMixedContentBlocker.cpp @@ -54,13 +54,6 @@ bool nsMixedContentBlocker::sBlockMixedScript = false; // Is mixed display content blocking (images, audio, video, <a ping>) enabled? bool nsMixedContentBlocker::sBlockMixedDisplay = false; -// Do we move HSTS before mixed-content -bool nsMixedContentBlocker::sUseHSTS = false; -// Do we send an HSTS priming request -bool nsMixedContentBlocker::sSendHSTSPriming = false; -// Default HSTS Priming failure timeout to 7 days, in seconds -uint32_t nsMixedContentBlocker::sHSTSPrimingCacheTimeout = (60 * 24 * 7); - // Fired at the document that attempted to load mixed content. The UI could // handle this event, for example, by displaying an info bar that offers the // choice to reload the page with mixed content permitted. @@ -202,18 +195,6 @@ nsMixedContentBlocker::nsMixedContentBlocker() // Cache the pref for mixed display blocking Preferences::AddBoolVarCache(&sBlockMixedDisplay, "security.mixed_content.block_display_content"); - - // Cache the pref for HSTS - Preferences::AddBoolVarCache(&sUseHSTS, - "security.mixed_content.use_hsts"); - - // Cache the pref for sending HSTS priming - Preferences::AddBoolVarCache(&sSendHSTSPriming, - "security.mixed_content.send_hsts_priming"); - - // Cache the pref for HSTS priming failure cache time - Preferences::AddUintVarCache(&sHSTSPrimingCacheTimeout, - "security.mixed_content.hsts_priming_cache_timeout"); } nsMixedContentBlocker::~nsMixedContentBlocker() @@ -343,22 +324,6 @@ nsMixedContentBlocker::AsyncOnChannelRedirect(nsIChannel* aOldChannel, return NS_BINDING_FAILED; } - if (nsMixedContentBlocker::sSendHSTSPriming) { - // The LoadInfo passed in is for the original channel, HSTS priming needs to - // be set on the new channel, if required. If the redirect changes - // http->https, or vice-versa, the need for priming may change. - nsCOMPtr<nsILoadInfo> newLoadInfo; - rv = aNewChannel->GetLoadInfo(getter_AddRefs(newLoadInfo)); - NS_ENSURE_SUCCESS(rv, rv); - rv = nsMixedContentBlocker::MarkLoadInfoForPriming(newUri, - requestingContext, - newLoadInfo); - if (NS_FAILED(rv)) { - decision = REJECT_REQUEST; - newLoadInfo->ClearHSTSPriming(); - } - } - // If the channel is about to load mixed content, abort the channel if (!NS_CP_ACCEPTED(decision)) { autoCallback.DontCallback(); @@ -503,6 +468,13 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, *aDecision = ACCEPT; return NS_OK; + // Creating insecure connections for a save-as link download is acceptable. + // This download is completely disconnected from the docShell, but still + // using the same loading principal. + case TYPE_SAVEAS_DOWNLOAD: + *aDecision = ACCEPT; + return NS_OK; + // Static display content is considered moderate risk for mixed content so // these will be blocked according to the mixed display preference case TYPE_IMAGE: @@ -691,12 +663,6 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, // the parent is https, and the protocol associated with innerContentLocation // doesn't map to the secure URI flags checked above. Assert this for // sanity's sake -#ifdef DEBUG - bool isHttpsScheme = false; - rv = innerContentLocation->SchemeIs("https", &isHttpsScheme); - NS_ENSURE_SUCCESS(rv, rv); - MOZ_ASSERT(!isHttpsScheme); -#endif *aDecision = REJECT_REQUEST; return NS_OK; } @@ -836,34 +802,6 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, } nsresult stateRV = securityUI->GetState(&state); - bool doHSTSPriming = false; - if (isHttpScheme) { - bool hsts = false; - bool cached = false; - nsCOMPtr<nsISiteSecurityService> sss = - do_GetService(NS_SSSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aContentLocation, - 0, &cached, &hsts); - NS_ENSURE_SUCCESS(rv, rv); - - if (hsts && sUseHSTS) { - // assume we will be upgraded later - *aDecision = ACCEPT; - return NS_OK; - } - - // Send a priming request if the result is not already cached and priming - // requests are allowed - if (!cached && sSendHSTSPriming) { - // add this URI as a priming location - doHSTSPriming = true; - document->AddHSTSPrimingLocation(innerContentLocation, - HSTSPrimingState::eHSTS_PRIMING_ALLOW); - *aDecision = ACCEPT; - } - } - // At this point we know that the request is mixed content, and the only // question is whether we block it. Record telemetry at this point as to // whether HSTS would have fixed things by making the content location @@ -879,14 +817,14 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, bool active = (classification == eMixedScript); if (!aHadInsecureImageRedirect) { if (XRE_IsParentProcess()) { - AccumulateMixedContentHSTS(innerContentLocation, active, doHSTSPriming); + AccumulateMixedContentHSTS(innerContentLocation, active); } else { // Ask the parent process to do the same call mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); if (cc) { mozilla::ipc::URIParams uri; SerializeURI(innerContentLocation, uri); - cc->SendAccumulateMixedContentHSTS(uri, active, doHSTSPriming); + cc->SendAccumulateMixedContentHSTS(uri, active); } } } @@ -929,13 +867,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, } } } else { - if (doHSTSPriming) { - document->AddHSTSPrimingLocation(innerContentLocation, - HSTSPrimingState::eHSTS_PRIMING_BLOCK); - *aDecision = nsIContentPolicy::ACCEPT; - } else { - *aDecision = nsIContentPolicy::REJECT_REQUEST; - } + *aDecision = nsIContentPolicy::REJECT_REQUEST; LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked); if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) { rootDoc->SetHasMixedDisplayContentBlocked(true); @@ -981,13 +913,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, } } else { //User has not overriden the pref by Disabling protection. Reject the request and update the security state. - if (doHSTSPriming) { - document->AddHSTSPrimingLocation(innerContentLocation, - HSTSPrimingState::eHSTS_PRIMING_BLOCK); - *aDecision = nsIContentPolicy::ACCEPT; - } else { - *aDecision = nsIContentPolicy::REJECT_REQUEST; - } + *aDecision = nsIContentPolicy::REJECT_REQUEST; LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked); // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. if (rootDoc->GetHasMixedActiveContentBlocked()) { @@ -1052,24 +978,10 @@ enum MixedContentHSTSState { MCB_HSTS_ACTIVE_WITH_HSTS = 3 }; -// Similar to the existing mixed-content HSTS, except MCB_HSTS_*_NO_HSTS is -// broken into two distinct states, indicating whether we plan to send a priming -// request or not. If we decided not go send a priming request, it could be -// because it is a type we do not support, or because we cached a previous -// negative response. -enum MixedContentHSTSPrimingState { - eMCB_HSTS_PASSIVE_WITH_HSTS = 0, - eMCB_HSTS_ACTIVE_WITH_HSTS = 1, - eMCB_HSTS_PASSIVE_NO_PRIMING = 2, - eMCB_HSTS_PASSIVE_DO_PRIMING = 3, - eMCB_HSTS_ACTIVE_NO_PRIMING = 4, - eMCB_HSTS_ACTIVE_DO_PRIMING = 5 -}; - // Record information on when HSTS would have made mixed content not mixed // content (regardless of whether it was actually blocked) void -nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive, bool aHasHSTSPriming) +nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive) { // This method must only be called in the parent, because // nsSiteSecurityService is only available in the parent @@ -1089,108 +1001,26 @@ nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive, bo return; } - // states: would upgrade, would prime, hsts info cached + // states: would upgrade, hsts info cached // active, passive // if (!aActive) { if (!hsts) { Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS, MCB_HSTS_PASSIVE_NO_HSTS); - if (aHasHSTSPriming) { - Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING, - eMCB_HSTS_PASSIVE_DO_PRIMING); - } else { - Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING, - eMCB_HSTS_PASSIVE_NO_PRIMING); - } } else { Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS, MCB_HSTS_PASSIVE_WITH_HSTS); - Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING, - eMCB_HSTS_PASSIVE_WITH_HSTS); } } else { if (!hsts) { Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS, MCB_HSTS_ACTIVE_NO_HSTS); - if (aHasHSTSPriming) { - Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING, - eMCB_HSTS_ACTIVE_DO_PRIMING); - } else { - Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING, - eMCB_HSTS_ACTIVE_NO_PRIMING); - } } else { Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS, MCB_HSTS_ACTIVE_WITH_HSTS); - Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING, - eMCB_HSTS_ACTIVE_WITH_HSTS); } } -} - -//static -nsresult -nsMixedContentBlocker::MarkLoadInfoForPriming(nsIURI* aURI, - nsISupports* aRequestingContext, - nsILoadInfo* aLoadInfo) -{ - nsresult rv; - bool sendPriming = false; - bool mixedContentWouldBlock = false; - rv = GetHSTSPrimingFromRequestingContext(aURI, - aRequestingContext, - &sendPriming, - &mixedContentWouldBlock); - NS_ENSURE_SUCCESS(rv, rv); - - if (sendPriming) { - aLoadInfo->SetHSTSPriming(mixedContentWouldBlock); - } - - return NS_OK; -} - -//static -nsresult -nsMixedContentBlocker::GetHSTSPrimingFromRequestingContext(nsIURI* aURI, - nsISupports* aRequestingContext, - bool* aSendPrimingRequest, - bool* aMixedContentWouldBlock) -{ - *aSendPrimingRequest = false; - *aMixedContentWouldBlock = false; - // If we marked for priming, we used the innermost URI, so get that - nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI); - if (!innerURI) { - NS_ERROR("Can't get innerURI from aContentLocation"); - return NS_ERROR_CONTENT_BLOCKED; - } - - bool isHttp = false; - innerURI->SchemeIs("http", &isHttp); - if (!isHttp) { - // there is nothign to do - return NS_OK; - } - - // If the DocShell was marked for HSTS priming, propagate that to the LoadInfo - nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext); - if (!docShell) { - return NS_OK; - } - nsCOMPtr<nsIDocument> document = docShell->GetDocument(); - if (!document) { - return NS_OK; - } - - HSTSPrimingState status = document->GetHSTSPrimingStateForLocation(innerURI); - if (status != HSTSPrimingState::eNO_HSTS_PRIMING) { - *aSendPrimingRequest = (status != HSTSPrimingState::eNO_HSTS_PRIMING); - *aMixedContentWouldBlock = (status == HSTSPrimingState::eHSTS_PRIMING_BLOCK); - } - - return NS_OK; -} +}
\ No newline at end of file diff --git a/dom/security/nsMixedContentBlocker.h b/dom/security/nsMixedContentBlocker.h index 539c3ebbb..56ab9621f 100644 --- a/dom/security/nsMixedContentBlocker.h +++ b/dom/security/nsMixedContentBlocker.h @@ -62,44 +62,11 @@ public: nsIPrincipal* aRequestPrincipal, int16_t* aDecision); static void AccumulateMixedContentHSTS(nsIURI* aURI, - bool aActive, - bool aHasHSTSPriming); - /* If the document associated with aRequestingContext requires priming for - * aURI, propagate that to the LoadInfo so the HttpChannel will find out about - * it. - * - * @param aURI The URI associated with the load - * @param aRequestingContext the requesting context passed to ShouldLoad - * @param aLoadInfo the LoadInfo for the load - */ - static nsresult MarkLoadInfoForPriming(nsIURI* aURI, - nsISupports* aRequestingContext, - nsILoadInfo* aLoadInfo); - - /* Given a context, return whether HSTS was marked on the document associated - * with the load for the given URI. This is used by MarkLoadInfoForPriming and - * directly by the image loader to determine whether to allow a load to occur - * from the cache. - * - * @param aURI The URI associated with the load - * @param aRequestingContext the requesting context passed to ShouldLoad - * @param aSendPrimingRequest out true if priming is required on the channel - * @param aMixedContentWouldBlock out true if mixed content would block - */ - static nsresult GetHSTSPrimingFromRequestingContext(nsIURI* aURI, - nsISupports* aRequestingContext, - bool* aSendPrimingRequest, - bool* aMixedContentWouldBlock); + bool aActive); static bool sBlockMixedScript; static bool sBlockMixedDisplay; - // Do we move HSTS before mixed-content - static bool sUseHSTS; - // Do we send an HSTS priming request - static bool sSendHSTSPriming; - // Default HSTS Priming failure timeout in seconds - static uint32_t sHSTSPrimingCacheTimeout; }; #endif /* nsMixedContentBlocker_h___ */ diff --git a/dom/security/test/csp/file_frame_ancestors_ro.html b/dom/security/test/csp/file_frame_ancestors_ro.html new file mode 100644 index 000000000..ff5ae9cf9 --- /dev/null +++ b/dom/security/test/csp/file_frame_ancestors_ro.html @@ -0,0 +1 @@ +<html><body>Child Document</body></html> diff --git a/dom/security/test/csp/file_frame_ancestors_ro.html^headers^ b/dom/security/test/csp/file_frame_ancestors_ro.html^headers^ new file mode 100644 index 000000000..d018af3a9 --- /dev/null +++ b/dom/security/test/csp/file_frame_ancestors_ro.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy-Report-Only: frame-ancestors 'none'; report-uri http://mochi.test:8888/foo.sjs 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 ca5c2c6ea..86b7fd0cd 100644 --- a/dom/security/test/csp/mochitest.ini +++ b/dom/security/test/csp/mochitest.ini @@ -88,9 +88,13 @@ 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^ + file_frame_ancestors_ro.html + file_frame_ancestors_ro.html^headers^ file_hash_source.html file_dual_header_testserver.sjs file_hash_source.html^headers^ @@ -240,8 +244,10 @@ skip-if = toolkit == 'android' # Times out, not sure why (bug 1008445) [test_bug910139.html] [test_bug909029.html] [test_bug1229639.html] +[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_frame_ancestors_ro.html b/dom/security/test/csp/test_frame_ancestors_ro.html new file mode 100644 index 000000000..90f68e25e --- /dev/null +++ b/dom/security/test/csp/test_frame_ancestors_ro.html @@ -0,0 +1,69 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for frame-ancestors support in Content-Security-Policy-Report-Only</title> + <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="cspframe"></iframe> +<script type="text/javascript"> +const docUri = "http://mochi.test:8888/tests/dom/security/test/csp/file_frame_ancestors_ro.html"; +const frame = document.getElementById("cspframe"); + +let testResults = { + reportFired: false, + frameLoaded: false +}; + +function checkResults(reportObj) { + let cspReport = reportObj["csp-report"]; + is(cspReport["document-uri"], docUri, "Incorrect document-uri"); + + // we can not test for the whole referrer since it includes platform specific information + is(cspReport["referrer"], document.location.toString(), "Incorrect referrer"); + is(cspReport["blocked-uri"], document.location.toString(), "Incorrect blocked-uri"); + is(cspReport["violated-directive"], "frame-ancestors 'none'", "Incorrect violated-directive"); + is(cspReport["original-policy"], "frame-ancestors 'none'; report-uri http://mochi.test:8888/foo.sjs", "Incorrect original-policy"); + testResults.reportFired = true; +} + +let chromeScriptUrl = SimpleTest.getTestFileURL("file_report_chromescript.js"); +let script = SpecialPowers.loadChromeScript(chromeScriptUrl); + +script.addMessageListener('opening-request-completed', function ml(msg) { + if (msg.error) { + ok(false, "Could not query report (exception: " + msg.error + ")"); + } else { + try { + let reportObj = JSON.parse(msg.report); + // test for the proper values in the report object + checkResults(reportObj); + } catch (e) { + ok(false, "Error verifying report object (exception: " + e + ")"); + } + } + + script.removeMessageListener('opening-request-completed', ml); + script.sendAsyncMessage("finish"); + checkTestResults(); +}); + +frame.addEventListener( 'load', () => { + // Make sure the frame is still loaded + testResults.frameLoaded = true; + checkTestResults() +} ); + +function checkTestResults() { + if( testResults.reportFired && testResults.frameLoaded ) { + SimpleTest.finish(); + } +} + +SimpleTest.waitForExplicitFinish(); +frame.src = docUri; + +</script> +</body> +</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/dom/security/test/csp/test_referrerdirective.html b/dom/security/test/csp/test_referrerdirective.html index 770fcc40b..f590460a0 100644 --- a/dom/security/test/csp/test_referrerdirective.html +++ b/dom/security/test/csp/test_referrerdirective.html @@ -116,8 +116,6 @@ SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv({ 'set': [['security.mixed_content.block_active_content', false], ['security.mixed_content.block_display_content', false], - ['security.mixed_content.send_hsts_priming', false], - ['security.mixed_content.use_hsts', false], ] }, function() { diff --git a/dom/security/test/general/browser.ini b/dom/security/test/general/browser.ini new file mode 100644 index 000000000..b00baa95d --- /dev/null +++ b/dom/security/test/general/browser.ini @@ -0,0 +1,14 @@ +[DEFAULT] +[browser_test_toplevel_data_navigations.js] +support-files = + file_toplevel_data_navigations.sjs + file_toplevel_data_meta_redirect.html +[browser_test_data_download.js] +support-files = + file_data_download.html +[browser_test_data_text_csv.js] +support-files = + file_data_text_csv.html +[browser_test_view_image_data_navigation.js] +support-files = + file_view_image_data_navigation.html diff --git a/dom/security/test/general/browser_test_data_download.js b/dom/security/test/general/browser_test_data_download.js new file mode 100644 index 000000000..1ee8d5844 --- /dev/null +++ b/dom/security/test/general/browser_test_data_download.js @@ -0,0 +1,37 @@ +"use strict"; + +const kTestPath = getRootDirectory(gTestPath) + .replace("chrome://mochitests/content", "http://example.com") +const kTestURI = kTestPath + "file_data_download.html"; + +function addWindowListener(aURL, aCallback) { + Services.wm.addListener({ + onOpenWindow(aXULWindow) { + info("window opened, waiting for focus"); + Services.wm.removeListener(this); + var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + waitForFocus(function() { + is(domwindow.document.location.href, aURL, "should have seen the right window open"); + aCallback(domwindow); + }, domwindow); + }, + onCloseWindow(aXULWindow) { }, + onWindowTitleChange(aXULWindow, aNewTitle) { } + }); +} + +function test() { + waitForExplicitFinish(); + Services.prefs.setBoolPref("security.data_uri.block_toplevel_data_uri_navigations", true); + registerCleanupFunction(function() { + Services.prefs.clearUserPref("security.data_uri.block_toplevel_data_uri_navigations"); + }); + addWindowListener("chrome://mozapps/content/downloads/unknownContentType.xul", function(win) { + is(win.document.getElementById("location").value, "data-foo.html", + "file name of download should match"); + win.close(); + finish(); + }); + gBrowser.loadURI(kTestURI); +} diff --git a/dom/security/test/general/browser_test_data_text_csv.js b/dom/security/test/general/browser_test_data_text_csv.js new file mode 100644 index 000000000..c45e40cc2 --- /dev/null +++ b/dom/security/test/general/browser_test_data_text_csv.js @@ -0,0 +1,37 @@ +"use strict"; + +const kTestPath = getRootDirectory(gTestPath) + .replace("chrome://mochitests/content", "http://example.com") +const kTestURI = kTestPath + "file_data_text_csv.html"; + +function addWindowListener(aURL, aCallback) { + Services.wm.addListener({ + onOpenWindow(aXULWindow) { + info("window opened, waiting for focus"); + Services.wm.removeListener(this); + var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + waitForFocus(function() { + is(domwindow.document.location.href, aURL, "should have seen the right window open"); + aCallback(domwindow); + }, domwindow); + }, + onCloseWindow(aXULWindow) { }, + onWindowTitleChange(aXULWindow, aNewTitle) { } + }); +} + +function test() { + waitForExplicitFinish(); + Services.prefs.setBoolPref("security.data_uri.block_toplevel_data_uri_navigations", true); + registerCleanupFunction(function() { + Services.prefs.clearUserPref("security.data_uri.block_toplevel_data_uri_navigations"); + }); + addWindowListener("chrome://mozapps/content/downloads/unknownContentType.xul", function(win) { + is(win.document.getElementById("location").value, "text/csv;foo,bar,foobar", + "file name of download should match"); + win.close(); + finish(); + }); + gBrowser.loadURI(kTestURI); +} diff --git a/dom/security/test/general/browser_test_toplevel_data_navigations.js b/dom/security/test/general/browser_test_toplevel_data_navigations.js new file mode 100644 index 000000000..a13a6350e --- /dev/null +++ b/dom/security/test/general/browser_test_toplevel_data_navigations.js @@ -0,0 +1,54 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +"use strict"; + +const kDataBody = "toplevel navigation to data: URI allowed"; +const kDataURI = "data:text/html,<body>" + kDataBody + "</body>"; +const kTestPath = getRootDirectory(gTestPath) + .replace("chrome://mochitests/content", "http://example.com") +const kRedirectURI = kTestPath + "file_toplevel_data_navigations.sjs"; +const kMetaRedirectURI = kTestPath + "file_toplevel_data_meta_redirect.html"; + +add_task(async function test_nav_data_uri() { + await SpecialPowers.pushPrefEnv({ + "set": [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + await BrowserTestUtils.withNewTab(kDataURI, async function(browser) { + await ContentTask.spawn(gBrowser.selectedBrowser, {kDataBody}, async function({kDataBody}) { // eslint-disable-line + is(content.document.body.innerHTML, kDataBody, + "data: URI navigation from system should be allowed"); + }); + }); +}); + +add_task(async function test_nav_data_uri_redirect() { + await SpecialPowers.pushPrefEnv({ + "set": [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + let tab = BrowserTestUtils.addTab(gBrowser, kRedirectURI); + registerCleanupFunction(async function() { + await BrowserTestUtils.removeTab(tab); + }); + // wait to make sure data: URI did not load before checking that it got blocked + await new Promise(resolve => setTimeout(resolve, 500)); + await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() { + is(content.document.body.innerHTML, "", + "data: URI navigation after server redirect should be blocked"); + }); +}); + +add_task(async function test_nav_data_uri_meta_redirect() { + await SpecialPowers.pushPrefEnv({ + "set": [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + let tab = BrowserTestUtils.addTab(gBrowser, kMetaRedirectURI); + registerCleanupFunction(async function() { + await BrowserTestUtils.removeTab(tab); + }); + // wait to make sure data: URI did not load before checking that it got blocked + await new Promise(resolve => setTimeout(resolve, 500)); + await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() { + is(content.document.body.innerHTML, "", + "data: URI navigation after meta redirect should be blocked"); + }); +}); diff --git a/dom/security/test/general/browser_test_view_image_data_navigation.js b/dom/security/test/general/browser_test_view_image_data_navigation.js new file mode 100644 index 000000000..22de35894 --- /dev/null +++ b/dom/security/test/general/browser_test_view_image_data_navigation.js @@ -0,0 +1,30 @@ +"use strict"; + +const TEST_PAGE = getRootDirectory(gTestPath) + "file_view_image_data_navigation.html"; + +add_task(async function test_principal_right_click_open_link_in_new_tab() { + await SpecialPowers.pushPrefEnv({ + "set": [["security.data_uri.block_toplevel_data_uri_navigations", true]], + }); + + await BrowserTestUtils.withNewTab(TEST_PAGE, async function(browser) { + let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, true); + + // simulate right-click->view-image + BrowserTestUtils.waitForEvent(document, "popupshown", false, event => { + // These are operations that must be executed synchronously with the event. + document.getElementById("context-viewimage").doCommand(); + event.target.hidePopup(); + return true; + }); + BrowserTestUtils.synthesizeMouseAtCenter("#testimage", + { type: "contextmenu", button: 2 }, + gBrowser.selectedBrowser); + await loadPromise; + + await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() { + ok(content.document.location.toString().startsWith("data:image/svg+xml;"), + "data:image/svg navigation allowed through right-click view-image") + }); + }); +}); diff --git a/dom/security/test/general/file_block_toplevel_data_navigation.html b/dom/security/test/general/file_block_toplevel_data_navigation.html new file mode 100644 index 000000000..5fbfdfdef --- /dev/null +++ b/dom/security/test/general/file_block_toplevel_data_navigation.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Toplevel data navigation</title> +</head> +<body> +test1: clicking data: URI tries to navigate window<br/> +<a id="testlink" href="data:text/html,<body>toplevel data: URI navigations should be blocked</body>">click me</a> +<script> + document.getElementById('testlink').click(); +</script> +</body> +</html> diff --git a/dom/security/test/general/file_block_toplevel_data_navigation2.html b/dom/security/test/general/file_block_toplevel_data_navigation2.html new file mode 100644 index 000000000..e0308e1ae --- /dev/null +++ b/dom/security/test/general/file_block_toplevel_data_navigation2.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Toplevel data navigation</title> +</head> +<body> +test2: data: URI in iframe tries to window.open(data:, _blank);<br/> +<iframe id="testFrame" src=""></iframe> +<script> + let DATA_URI = `data:text/html,<body><script> + var win = window.open("data:text/html,<body>toplevel data: URI navigations should be blocked</body>", "_blank"); + setTimeout(function () { + var result = win.document.body.innerHTML === "" ? "blocked" : "navigated"; + parent.postMessage(result, "*"); + win.close(); + }, 1000); + <\/script></body>`; + + window.addEventListener("message", receiveMessage); + function receiveMessage(event) { + window.removeEventListener("message", receiveMessage); + // propagate the information back to the caller + window.opener.postMessage(event.data, "*"); + } + document.getElementById('testFrame').src = DATA_URI; +</script> +</body> +</html> diff --git a/dom/security/test/general/file_block_toplevel_data_navigation3.html b/dom/security/test/general/file_block_toplevel_data_navigation3.html new file mode 100644 index 000000000..34aeddab3 --- /dev/null +++ b/dom/security/test/general/file_block_toplevel_data_navigation3.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Toplevel data navigation</title> +</head> +<body> +test3: performing data: URI navigation through win.loc.href<br/> +<script> + window.location.href = "data:text/html,<body>toplevel data: URI navigations should be blocked</body>"; +</script> +</body> +</html> diff --git a/dom/security/test/general/file_block_toplevel_data_redirect.sjs b/dom/security/test/general/file_block_toplevel_data_redirect.sjs new file mode 100644 index 000000000..64e294cab --- /dev/null +++ b/dom/security/test/general/file_block_toplevel_data_redirect.sjs @@ -0,0 +1,14 @@ +// Custom *.sjs file specifically for the needs of Bug: +// Bug 1394554 - Block toplevel data: URI navigations after redirect + +var DATA_URI = + "<body>toplevel data: URI navigations after redirect should be blocked</body>"; + +function handleRequest(request, response) +{ + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", "data:text/html," + escape(DATA_URI), false); +} diff --git a/dom/security/test/general/file_data_download.html b/dom/security/test/general/file_data_download.html new file mode 100644 index 000000000..4cc92fe8f --- /dev/null +++ b/dom/security/test/general/file_data_download.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test download attribute for data: URI</title> +</head> +<body> + <a href="data:text/html,<body>data download</body>" download="data-foo.html" id="testlink">download data</a> + <script> + // click the link to have the downoad panel appear + let testlink = document.getElementById("testlink"); + testlink.click(); + </script> + </body> +</html> diff --git a/dom/security/test/general/file_data_text_csv.html b/dom/security/test/general/file_data_text_csv.html new file mode 100644 index 000000000..a9ac369d1 --- /dev/null +++ b/dom/security/test/general/file_data_text_csv.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test open data:text/csv</title> +</head> +<body> + <a href="data:text/csv;foo,bar,foobar" id="testlink">test text/csv</a> + <script> + // click the link to have the downoad panel appear + let testlink = document.getElementById("testlink"); + testlink.click(); + </script> + </body> +</html> diff --git a/dom/security/test/general/file_toplevel_data_meta_redirect.html b/dom/security/test/general/file_toplevel_data_meta_redirect.html new file mode 100644 index 000000000..f4f5deb52 --- /dev/null +++ b/dom/security/test/general/file_toplevel_data_meta_redirect.html @@ -0,0 +1,10 @@ +<html>
+<body>
+<head>
+ <meta http-equiv="refresh"
+ content="0; url='data:text/html,<body>toplevel meta redirect to data: URI should be blocked</body>'">
+</head>
+<body>
+Meta Redirect to data: URI
+</body>
+</html>
diff --git a/dom/security/test/general/file_toplevel_data_navigations.sjs b/dom/security/test/general/file_toplevel_data_navigations.sjs new file mode 100644 index 000000000..501b833e5 --- /dev/null +++ b/dom/security/test/general/file_toplevel_data_navigations.sjs @@ -0,0 +1,14 @@ +// Custom *.sjs file specifically for the needs of Bug: +// Bug 1394554 - Block toplevel data: URI navigations after redirect + +var DATA_URI = + "data:text/html,<body>toplevel data: URI navigations after redirect should be blocked</body>"; + +function handleRequest(request, response) +{ + // avoid confusing cache behaviors + response.setHeader("Cache-Control", "no-cache", false); + + response.setStatusLine("1.1", 302, "Found"); + response.setHeader("Location", DATA_URI, false); +} diff --git a/dom/security/test/general/file_view_image_data_navigation.html b/dom/security/test/general/file_view_image_data_navigation.html new file mode 100644 index 000000000..a3f9acfb4 --- /dev/null +++ b/dom/security/test/general/file_view_image_data_navigation.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1407891: Test navigation for right-click view-image on data:image/svg</title> +</head> +<body> + +<img id="testimage" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNOCwxMkwzLDcsNCw2bDQsNCw0LTQsMSwxWiIgZmlsbD0iIzZBNkE2QSIgLz4KPC9zdmc+Cg=="></img> + +</body> +</html> diff --git a/dom/security/test/general/mochitest.ini b/dom/security/test/general/mochitest.ini index 70c0c9fb6..f3bcca072 100644 --- a/dom/security/test/general/mochitest.ini +++ b/dom/security/test/general/mochitest.ini @@ -3,7 +3,19 @@ support-files = file_contentpolicytype_targeted_link_iframe.sjs file_nosniff_testserver.sjs file_block_script_wrong_mime_server.sjs + file_block_toplevel_data_navigation.html + file_block_toplevel_data_navigation2.html + file_block_toplevel_data_navigation3.html + file_block_toplevel_data_redirect.sjs [test_contentpolicytype_targeted_link_iframe.html] [test_nosniff.html] [test_block_script_wrong_mime.html] +[test_block_toplevel_data_navigation.html] +skip-if = toolkit == 'android' # intermittent failure +[test_block_toplevel_data_img_navigation.html] +skip-if = toolkit == 'android' # intermittent failure +[test_allow_opening_data_pdf.html] +skip-if = toolkit == 'android' +[test_allow_opening_data_json.html] +skip-if = toolkit == 'android' diff --git a/dom/security/test/general/test_allow_opening_data_json.html b/dom/security/test/general/test_allow_opening_data_json.html new file mode 100644 index 000000000..1530a24e8 --- /dev/null +++ b/dom/security/test/general/test_allow_opening_data_json.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1403814: Allow toplevel data URI navigation data:application/json</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function test_toplevel_data_json() { + const DATA_JSON = "data:application/json,{'my_json_key':'my_json_value'}"; + + let win = window.open(DATA_JSON); + let wrappedWin = SpecialPowers.wrap(win); + + // Unfortunately we can't detect whether the JSON has loaded or not using some + // event, hence we are constantly polling location.href till we see that + // the data: URI appears. Test times out on failure. + var jsonLoaded = setInterval(function() { + if (wrappedWin.document.location.href.startsWith("data:application/json")) { + clearInterval(jsonLoaded); + ok(true, "navigating to data:application/json allowed"); + wrappedWin.close(); + SimpleTest.finish(); + } + }, 200); +} + +SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]] +}, test_toplevel_data_json); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_allow_opening_data_pdf.html b/dom/security/test/general/test_allow_opening_data_pdf.html new file mode 100644 index 000000000..6b51fe57b --- /dev/null +++ b/dom/security/test/general/test_allow_opening_data_pdf.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1398692: Allow toplevel navigation to a data:application/pdf</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function test_toplevel_data_pdf() { + // The PDF contains one page and it is a 3/72" square, the minimum allowed by the spec + const DATA_PDF = + "data:application/pdf;base64,JVBERi0xLjANCjEgMCBvYmo8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFI+PmVuZG9iaiAyIDAgb2JqPDwvVHlwZS9QYWdlcy9LaWRzWzMgMCBSXS9Db3VudCAxPj5lbmRvYmogMyAwIG9iajw8L1R5cGUvUGFnZS9NZWRpYUJveFswIDAgMyAzXT4+ZW5kb2JqDQp4cmVmDQowIDQNCjAwMDAwMDAwMDAgNjU1MzUgZg0KMDAwMDAwMDAxMCAwMDAwMCBuDQowMDAwMDAwMDUzIDAwMDAwIG4NCjAwMDAwMDAxMDIgMDAwMDAgbg0KdHJhaWxlcjw8L1NpemUgNC9Sb290IDEgMCBSPj4NCnN0YXJ0eHJlZg0KMTQ5DQolRU9G"; + + let win = window.open(DATA_PDF); + let wrappedWin = SpecialPowers.wrap(win); + + // Unfortunately we can't detect whether the PDF has loaded or not using some + // event, hence we are constantly polling location.href till we see that + // the data: URI appears. Test times out on failure. + var pdfLoaded = setInterval(function() { + if (wrappedWin.document.location.href.startsWith("data:application/pdf")) { + clearInterval(pdfLoaded); + ok(true, "navigating to data:application/pdf allowed"); + wrappedWin.close(); + SimpleTest.finish(); + } + }, 200); +} + +SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", true]] +}, test_toplevel_data_pdf); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_block_toplevel_data_img_navigation.html b/dom/security/test/general/test_block_toplevel_data_img_navigation.html new file mode 100644 index 000000000..7f8dfc748 --- /dev/null +++ b/dom/security/test/general/test_block_toplevel_data_img_navigation.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1396798: Do not block toplevel data: navigation to image (except svgs)</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<script class="testbody" type="text/javascript"> +SpecialPowers.setBoolPref("security.data_uri.block_toplevel_data_uri_navigations", true); +SimpleTest.registerCleanupFunction(() => { + SpecialPowers.clearUserPref("security.data_uri.block_toplevel_data_uri_navigations"); +}); + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("have to test that top level data:image loading is blocked/allowed"); + +function test_toplevel_data_image() { + const DATA_PNG = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; + let win1 = window.open(DATA_PNG); + let wrappedWin1 = SpecialPowers.wrap(win1); + setTimeout(function () { + let images = wrappedWin1.document.getElementsByTagName('img'); + is(images.length, 1, "Loading data:image/png should be allowed"); + is(images[0].src, DATA_PNG, "Sanity: img src matches"); + wrappedWin1.close(); + test_toplevel_data_image_svg(); + }, 1000); +} + +function test_toplevel_data_image_svg() { + const DATA_SVG = + "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNOCwxMkwzLDcsNCw2bDQsNCw0LTQsMSwxWiIgZmlsbD0iIzZBNkE2QSIgLz4KPC9zdmc+Cg=="; + let win2 = window.open(DATA_SVG); + // Unfortunately we can't detect whether the window was closed using some event, + // hence we are constantly polling till we see that win == null. + // Test times out on failure. + var win2Closed = setInterval(function() { + if (win2 == null || win2.closed) { + clearInterval(win2Closed); + ok(true, "Loading data:image/svg+xml should be blocked"); + SimpleTest.finish(); + } + }, 200); +} +// fire up the tests +test_toplevel_data_image(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_block_toplevel_data_navigation.html b/dom/security/test/general/test_block_toplevel_data_navigation.html new file mode 100644 index 000000000..cef232b65 --- /dev/null +++ b/dom/security/test/general/test_block_toplevel_data_navigation.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1331351 - Block top level window data: URI navigations</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> +<script class="testbody" type="text/javascript"> +SpecialPowers.setBoolPref("security.data_uri.block_toplevel_data_uri_navigations", true); +SimpleTest.registerCleanupFunction(() => { + SpecialPowers.clearUserPref("security.data_uri.block_toplevel_data_uri_navigations"); +}); + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("have to test that top level data: URI navgiation is blocked"); + +function test1() { + // simple data: URI click navigation should be prevented + let TEST_FILE = "file_block_toplevel_data_navigation.html"; + let win1 = window.open(TEST_FILE); + setTimeout(function () { + ok(SpecialPowers.wrap(win1).document.body.innerHTML.indexOf("test1:") !== -1, + "toplevel data: URI navigation through click() should be blocked"); + win1.close(); + test2(); + }, 1000); +} + +function test2() { + // data: URI in iframe which opens data: URI in _blank should be blocked + let win2 = window.open("file_block_toplevel_data_navigation2.html"); + window.addEventListener("message", receiveMessage); + function receiveMessage(event) { + window.removeEventListener("message", receiveMessage); + is(event.data, "blocked", + "data: URI navigation using _blank from data: URI should be blocked"); + win2.close(); + test3(); + } +} + +function test3() { + // navigating to a data: URI using window.location.href should be blocked + let win3 = window.open("file_block_toplevel_data_navigation3.html"); + setTimeout(function () { + ok(win3.document.body.innerHTML.indexOf("test3:") !== -1, + "data: URI navigation through win.loc.href should be blocked"); + win3.close(); + test4(); + }, 1000); +} + +function test4() { + // navigating to a data: URI using window.open() should be blocked + let win4 = window.open("data:text/html,<body>toplevel data: URI navigations should be blocked</body>"); + setTimeout(function () { + // Please note that the data: URI will be displayed in the URL-Bar but not + // loaded, hence we rather rely on document.body than document.location + is(win4.document.body.innerHTML, "", + "navigating to a data: URI using window.open() should be blocked"); + test5(); + }, 1000); +} + +function test5() { + // navigating to a URI which redirects to a data: URI using window.open() should be blocked + let win5 = window.open("file_block_toplevel_data_redirect.sjs"); + setTimeout(function () { + // Please note that the data: URI will be displayed in the URL-Bar but not + // loaded, hence we rather rely on document.body than document.location + is(SpecialPowers.wrap(win5).document.body.innerHTML, "", + "navigating to URI which redirects to a data: URI using window.open() should be blocked"); + win5.close(); + SimpleTest.finish(); + }, 1000); +} + +// fire up the tests +test1(); + +</script> +</body> +</html> diff --git a/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html index 7b1ab72dc..3ef243824 100644 --- a/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html +++ b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html @@ -61,6 +61,7 @@ var policy = { "content policy type should TYPESUBDOCUMENT"); categoryManager.deleteCategoryEntry("content-policy", POLICYNAME, false); SimpleTest.finish(); + return Ci.nsIContentPolicy.REJECT_REQUEST; } return Ci.nsIContentPolicy.ACCEPT; }, diff --git a/dom/security/test/hsts/browser.ini b/dom/security/test/hsts/browser.ini deleted file mode 100644 index ae75031df..000000000 --- a/dom/security/test/hsts/browser.ini +++ /dev/null @@ -1,19 +0,0 @@ -[DEFAULT] -skip-if = debug # bug 1311599, bug 1311239, etc -support-files = - head.js - file_priming-top.html - file_testserver.sjs - file_1x1.png - file_priming.js - file_stylesheet.css - -[browser_hsts-priming_allow_active.js] -[browser_hsts-priming_block_active.js] -[browser_hsts-priming_hsts_after_mixed.js] -[browser_hsts-priming_allow_display.js] -[browser_hsts-priming_block_display.js] -[browser_hsts-priming_block_active_css.js] -[browser_hsts-priming_block_active_with_redir_same.js] -[browser_hsts-priming_no-duplicates.js] -[browser_hsts-priming_cache-timeout.js] diff --git a/dom/security/test/hsts/browser_hsts-priming_allow_active.js b/dom/security/test/hsts/browser_hsts-priming_allow_active.js deleted file mode 100644 index a932b31b3..000000000 --- a/dom/security/test/hsts/browser_hsts-priming_allow_active.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Description of the test: - * Check that HSTS priming occurs correctly with mixed content when active - * content is allowed. - */ -'use strict'; - -//jscs:disable -add_task(function*() { - //jscs:enable - Services.obs.addObserver(Observer, "console-api-log-event", false); - Services.obs.addObserver(Observer, "http-on-examine-response", false); - registerCleanupFunction(do_cleanup); - - let which = "allow_active"; - - SetupPrefTestEnvironment(which); - - for (let server of Object.keys(test_servers)) { - yield execute_test(server, test_settings[which].mimetype); - } - - SpecialPowers.popPrefEnv(); -}); diff --git a/dom/security/test/hsts/browser_hsts-priming_allow_display.js b/dom/security/test/hsts/browser_hsts-priming_allow_display.js deleted file mode 100644 index 06546ca65..000000000 --- a/dom/security/test/hsts/browser_hsts-priming_allow_display.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Description of the test: - * Check that HSTS priming occurs correctly with mixed content when display - * content is allowed. - */ -'use strict'; - -//jscs:disable -add_task(function*() { - //jscs:enable - Services.obs.addObserver(Observer, "console-api-log-event", false); - Services.obs.addObserver(Observer, "http-on-examine-response", false); - registerCleanupFunction(do_cleanup); - - let which = "allow_display"; - - SetupPrefTestEnvironment(which); - - for (let server of Object.keys(test_servers)) { - yield execute_test(server, test_settings[which].mimetype); - } - - SpecialPowers.popPrefEnv(); -}); diff --git a/dom/security/test/hsts/browser_hsts-priming_block_active.js b/dom/security/test/hsts/browser_hsts-priming_block_active.js deleted file mode 100644 index a5478b185..000000000 --- a/dom/security/test/hsts/browser_hsts-priming_block_active.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Description of the test: - * Check that HSTS priming occurs correctly with mixed content when active - * content is blocked. - */ -'use strict'; - -//jscs:disable -add_task(function*() { - //jscs:enable - Services.obs.addObserver(Observer, "console-api-log-event", false); - Services.obs.addObserver(Observer, "http-on-examine-response", false); - registerCleanupFunction(do_cleanup); - - let which = "block_active"; - - SetupPrefTestEnvironment(which); - - for (let server of Object.keys(test_servers)) { - yield execute_test(server, test_settings[which].mimetype); - } - - SpecialPowers.popPrefEnv(); -}); diff --git a/dom/security/test/hsts/browser_hsts-priming_block_active_css.js b/dom/security/test/hsts/browser_hsts-priming_block_active_css.js deleted file mode 100644 index 340d11483..000000000 --- a/dom/security/test/hsts/browser_hsts-priming_block_active_css.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Description of the test: - * Check that HSTS priming occurs correctly with mixed content when active - * content is blocked for css. - */ -'use strict'; - -//jscs:disable -add_task(function*() { - //jscs:enable - Services.obs.addObserver(Observer, "console-api-log-event", false); - Services.obs.addObserver(Observer, "http-on-examine-response", false); - registerCleanupFunction(do_cleanup); - - let which = "block_active_css"; - - SetupPrefTestEnvironment(which); - - for (let server of Object.keys(test_servers)) { - yield execute_test(server, test_settings[which].mimetype); - } - - SpecialPowers.popPrefEnv(); -}); diff --git a/dom/security/test/hsts/browser_hsts-priming_block_active_with_redir_same.js b/dom/security/test/hsts/browser_hsts-priming_block_active_with_redir_same.js deleted file mode 100644 index 130a3d5ec..000000000 --- a/dom/security/test/hsts/browser_hsts-priming_block_active_with_redir_same.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Description of the test: - * Check that HSTS priming occurs correctly with mixed content when active - * content is blocked and redirect to the same host should still upgrade. - */ -'use strict'; - -//jscs:disable -add_task(function*() { - //jscs:enable - Services.obs.addObserver(Observer, "console-api-log-event", false); - Services.obs.addObserver(Observer, "http-on-examine-response", false); - registerCleanupFunction(do_cleanup); - - let which = "block_active_with_redir_same"; - - SetupPrefTestEnvironment(which); - - for (let server of Object.keys(test_servers)) { - yield execute_test(server, test_settings[which].mimetype); - } - - SpecialPowers.popPrefEnv(); -}); diff --git a/dom/security/test/hsts/browser_hsts-priming_block_display.js b/dom/security/test/hsts/browser_hsts-priming_block_display.js deleted file mode 100644 index 4eca62718..000000000 --- a/dom/security/test/hsts/browser_hsts-priming_block_display.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Description of the test: - * Check that HSTS priming occurs correctly with mixed content when display - * content is blocked. - */ -'use strict'; - -//jscs:disable -add_task(function*() { - //jscs:enable - Services.obs.addObserver(Observer, "console-api-log-event", false); - Services.obs.addObserver(Observer, "http-on-examine-response", false); - registerCleanupFunction(do_cleanup); - - let which = "block_display"; - - SetupPrefTestEnvironment(which); - - for (let server of Object.keys(test_servers)) { - yield execute_test(server, test_settings[which].mimetype); - } - - SpecialPowers.popPrefEnv(); -}); diff --git a/dom/security/test/hsts/browser_hsts-priming_cache-timeout.js b/dom/security/test/hsts/browser_hsts-priming_cache-timeout.js deleted file mode 100644 index 5416a71d2..000000000 --- a/dom/security/test/hsts/browser_hsts-priming_cache-timeout.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Description of the test: - * Test that the network.hsts_priming.cache_timeout preferene causes the cache - * to timeout - */ -'use strict'; - -//jscs:disable -add_task(function*() { - //jscs:enable - Observer.add_observers(Services); - registerCleanupFunction(do_cleanup); - - let which = "block_display"; - - SetupPrefTestEnvironment(which, [["security.mixed_content.hsts_priming_cache_timeout", 1]]); - - yield execute_test("no-ssl", test_settings[which].mimetype); - - let pre_promise = performance.now(); - - while ((performance.now() - pre_promise) < 2000) { - yield new Promise(function (resolve) { - setTimeout(resolve, 2000); - }); - } - - // clear the fact that we saw a priming request - test_settings[which].priming = {}; - - yield execute_test("no-ssl", test_settings[which].mimetype); - is(test_settings[which].priming["no-ssl"], true, - "Correctly send a priming request after expiration."); - - SpecialPowers.popPrefEnv(); -}); diff --git a/dom/security/test/hsts/browser_hsts-priming_hsts_after_mixed.js b/dom/security/test/hsts/browser_hsts-priming_hsts_after_mixed.js deleted file mode 100644 index 89ea6fbeb..000000000 --- a/dom/security/test/hsts/browser_hsts-priming_hsts_after_mixed.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Description of the test: - * Check that HSTS priming occurs correctly with mixed content when the - * mixed-content blocks before HSTS. - */ -'use strict'; - -//jscs:disable -add_task(function*() { - //jscs:enable - Services.obs.addObserver(Observer, "console-api-log-event", false); - Services.obs.addObserver(Observer, "http-on-examine-response", false); - registerCleanupFunction(do_cleanup); - - let which = "hsts_after_mixed"; - - SetupPrefTestEnvironment(which); - - for (let server of Object.keys(test_servers)) { - yield execute_test(server, test_settings[which].mimetype); - } - - SpecialPowers.popPrefEnv(); -}); diff --git a/dom/security/test/hsts/browser_hsts-priming_no-duplicates.js b/dom/security/test/hsts/browser_hsts-priming_no-duplicates.js deleted file mode 100644 index 3846fe4f0..000000000 --- a/dom/security/test/hsts/browser_hsts-priming_no-duplicates.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Description of the test: - * Only one request should be sent per host, even if we run the test more - * than once. - */ -'use strict'; - -//jscs:disable -add_task(function*() { - //jscs:enable - Observer.add_observers(Services); - registerCleanupFunction(do_cleanup); - - let which = "block_display"; - - SetupPrefTestEnvironment(which); - - for (let server of Object.keys(test_servers)) { - yield execute_test(server, test_settings[which].mimetype); - } - - test_settings[which].priming = {}; - - // run the tests twice to validate the cache is being used - for (let server of Object.keys(test_servers)) { - yield execute_test(server, test_settings[which].mimetype); - } - - SpecialPowers.popPrefEnv(); -}); diff --git a/dom/security/test/hsts/file_1x1.png b/dom/security/test/hsts/file_1x1.png Binary files differdeleted file mode 100644 index 1ba31ba1a..000000000 --- a/dom/security/test/hsts/file_1x1.png +++ /dev/null diff --git a/dom/security/test/hsts/file_priming-top.html b/dom/security/test/hsts/file_priming-top.html deleted file mode 100644 index b1d1bfa40..000000000 --- a/dom/security/test/hsts/file_priming-top.html +++ /dev/null @@ -1,84 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <title>Bug 1246540</title> - <meta http-equiv='content-type' content="text/html;charset=utf-8" /> -</head> -<body> - <p id="display"></p> - <div id="content" style="visibility: hidden"> - </div> - -<script type="text/javascript"> -/* - * Description of the test: - * Attempt to load an insecure resource. If the resource responds to HSTS - * priming with an STS header, the load should continue securely. - * If it does not, the load should continue be blocked or continue insecurely. - */ - -function parse_query_string() { - var q = {}; - document.location.search.substr(1). - split('&').forEach(function (item, idx, ar) { - let [k, v] = item.split('='); - q[k] = unescape(v); - }); - return q; -} - -var args = parse_query_string(); - -var subresources = { - css: { mimetype: 'text/css', file: 'file_stylesheet.css' }, - img: { mimetype: 'image/png', file: 'file_1x1.png' }, - script: { mimetype: 'text/javascript', file: 'file_priming.js' }, -}; - -function handler(ev) { - console.log("HSTS_PRIMING: Blocked "+args.id); -} - -function loadCss(src) { - let head = document.getElementsByTagName("head")[0]; - let link = document.createElement("link"); - link.setAttribute("rel", "stylesheet"); - link.setAttribute("type", subresources[args.type].mimetype); - link.setAttribute("href", src); - head.appendChild(link); -} - -function loadResource(src) { - let content = document.getElementById("content"); - let testElem = document.createElement(args.type); - testElem.setAttribute("id", args.id); - testElem.setAttribute("charset", "UTF-8"); - testElem.onerror = handler; - content.appendChild(testElem); - testElem.src = src; -} - -function loadTest() { - let subresource = subresources[args.type]; - - let src = "http://" - + args.host - + "/browser/dom/security/test/hsts/file_testserver.sjs" - + "?file=" +escape("browser/dom/security/test/hsts/" + subresource.file) - + "&primer=" + escape(args.id) - + "&mimetype=" + escape(subresource.mimetype) - ; - if (args.type == 'css') { - loadCss(src); - return; - } - - loadResource(src); -} - -// start running the tests -loadTest(); - -</script> -</body> -</html> diff --git a/dom/security/test/hsts/file_priming.js b/dom/security/test/hsts/file_priming.js deleted file mode 100644 index 023022da6..000000000 --- a/dom/security/test/hsts/file_priming.js +++ /dev/null @@ -1,4 +0,0 @@ -function completed() { - return; -} -completed(); diff --git a/dom/security/test/hsts/file_stylesheet.css b/dom/security/test/hsts/file_stylesheet.css deleted file mode 100644 index e69de29bb..000000000 --- a/dom/security/test/hsts/file_stylesheet.css +++ /dev/null diff --git a/dom/security/test/hsts/file_testserver.sjs b/dom/security/test/hsts/file_testserver.sjs deleted file mode 100644 index d5cd6b17a..000000000 --- a/dom/security/test/hsts/file_testserver.sjs +++ /dev/null @@ -1,66 +0,0 @@ -// SJS file for HSTS mochitests - -Components.utils.import("resource://gre/modules/NetUtil.jsm"); -Components.utils.importGlobalProperties(["URLSearchParams"]); - -function loadFromFile(path) { - // Load the HTML to return in the response from file. - // Since it's relative to the cwd of the test runner, we start there and - // append to get to the actual path of the file. - var testFile = - Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsILocalFile); - var dirs = path.split("/"); - for (var i = 0; i < dirs.length; i++) { - testFile.append(dirs[i]); - } - var testFileStream = - Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); - testFileStream.init(testFile, -1, 0, 0); - var test = NetUtil.readInputStreamToString(testFileStream, testFileStream.available()); - return test; -} - -function handleRequest(request, response) -{ - const query = new URLSearchParams(request.queryString); - - redir = query.get('redir'); - if (redir == 'same') { - query.delete("redir"); - response.setStatus(302); - let newURI = request.uri; - newURI.queryString = query.serialize(); - response.setHeader("Location", newURI.spec) - } - - // avoid confusing cache behaviors - response.setHeader("Cache-Control", "no-cache", false); - - // if we have a priming header, check for required behavior - // and set header appropriately - if (request.hasHeader('Upgrade-Insecure-Requests')) { - var expected = query.get('primer'); - if (expected == 'prime-hsts') { - // set it for 5 minutes - response.setHeader("Strict-Transport-Security", "max-age="+(60*5), false); - } else if (expected == 'reject-upgrade') { - response.setHeader("Strict-Transport-Security", "max-age=0", false); - } - response.write(''); - return; - } - - var file = query.get('file'); - if (file) { - var mimetype = unescape(query.get('mimetype')); - response.setHeader("Content-Type", mimetype, false); - response.write(loadFromFile(unescape(file))); - return; - } - - response.setHeader("Content-Type", "application/json", false); - response.write('{}'); -} diff --git a/dom/security/test/hsts/head.js b/dom/security/test/hsts/head.js deleted file mode 100644 index 362b36444..000000000 --- a/dom/security/test/hsts/head.js +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Description of the tests: - * Check that HSTS priming occurs correctly with mixed content - * - * This test uses three hostnames, each of which treats an HSTS priming - * request differently. - * * no-ssl never returns an ssl response - * * reject-upgrade returns an ssl response, but with no STS header - * * prime-hsts returns an ssl response with the appropriate STS header - * - * For each server, test that it response appropriately when the we allow - * or block active or display content, as well as when we send an hsts priming - * request, but do not change the order of mixed-content and HSTS. - * - * Test use http-on-examine-response, so must be run in browser context. - */ -'use strict'; - -var TOP_URI = "https://example.com/browser/dom/security/test/hsts/file_priming-top.html"; - -var test_servers = { - // a test server that does not support TLS - 'no-ssl': { - host: 'example.co.jp', - response: false, - id: 'no-ssl', - }, - // a test server which does not support STS upgrade - 'reject-upgrade': { - host: 'example.org', - response: true, - id: 'reject-upgrade', - }, - // a test server when sends an STS header when priming - 'prime-hsts': { - host: 'test1.example.com', - response: true, - id: 'prime-hsts' - }, -}; - -var test_settings = { - // mixed active content is allowed, priming will upgrade - allow_active: { - block_active: false, - block_display: false, - use_hsts: true, - send_hsts_priming: true, - type: 'script', - result: { - 'no-ssl': 'insecure', - 'reject-upgrade': 'insecure', - 'prime-hsts': 'secure', - }, - }, - // mixed active content is blocked, priming will upgrade - block_active: { - block_active: true, - block_display: false, - use_hsts: true, - send_hsts_priming: true, - type: 'script', - result: { - 'no-ssl': 'blocked', - 'reject-upgrade': 'blocked', - 'prime-hsts': 'secure', - }, - }, - // keep the original order of mixed-content and HSTS, but send - // priming requests - hsts_after_mixed: { - block_active: true, - block_display: false, - use_hsts: false, - send_hsts_priming: true, - type: 'script', - result: { - 'no-ssl': 'blocked', - 'reject-upgrade': 'blocked', - 'prime-hsts': 'blocked', - }, - }, - // mixed display content is allowed, priming will upgrade - allow_display: { - block_active: true, - block_display: false, - use_hsts: true, - send_hsts_priming: true, - type: 'img', - result: { - 'no-ssl': 'insecure', - 'reject-upgrade': 'insecure', - 'prime-hsts': 'secure', - }, - }, - // mixed display content is blocked, priming will upgrade - block_display: { - block_active: true, - block_display: true, - use_hsts: true, - send_hsts_priming: true, - type: 'img', - result: { - 'no-ssl': 'blocked', - 'reject-upgrade': 'blocked', - 'prime-hsts': 'secure', - }, - }, - // mixed active content is blocked, priming will upgrade (css) - block_active_css: { - block_active: true, - block_display: false, - use_hsts: true, - send_hsts_priming: true, - type: 'css', - result: { - 'no-ssl': 'blocked', - 'reject-upgrade': 'blocked', - 'prime-hsts': 'secure', - }, - }, - // mixed active content is blocked, priming will upgrade - // redirect to the same host - block_active_with_redir_same: { - block_active: true, - block_display: false, - use_hsts: true, - send_hsts_priming: true, - type: 'script', - redir: 'same', - result: { - 'no-ssl': 'blocked', - 'reject-upgrade': 'blocked', - 'prime-hsts': 'secure', - }, - }, -} -// track which test we are on -var which_test = ""; - -const Observer = { - observe: function (subject, topic, data) { - switch (topic) { - case 'console-api-log-event': - return Observer.console_api_log_event(subject, topic, data); - case 'http-on-examine-response': - return Observer.http_on_examine_response(subject, topic, data); - case 'http-on-modify-request': - return Observer.http_on_modify_request(subject, topic, data); - } - throw "Can't handle topic "+topic; - }, - add_observers: function (services) { - services.obs.addObserver(Observer, "console-api-log-event", false); - services.obs.addObserver(Observer, "http-on-examine-response", false); - services.obs.addObserver(Observer, "http-on-modify-request", false); - }, - // When a load is blocked which results in an error event within a page, the - // test logs to the console. - console_api_log_event: function (subject, topic, data) { - var message = subject.wrappedJSObject.arguments[0]; - // when we are blocked, this will match the message we sent to the console, - // ignore everything else. - var re = RegExp(/^HSTS_PRIMING: Blocked ([-\w]+).*$/); - if (!re.test(message)) { - return; - } - - let id = message.replace(re, '$1'); - let curTest =test_servers[id]; - - if (!curTest) { - ok(false, "HSTS priming got a console message blocked, "+ - "but doesn't match expectations "+id+" (msg="+message); - return; - } - - is("blocked", test_settings[which_test].result[curTest.id], "HSTS priming "+ - which_test+":"+curTest.id+" expected "+ - test_settings[which_test].result[curTest.id]+", got blocked"); - test_settings[which_test].finished[curTest.id] = "blocked"; - }, - get_current_test: function(uri) { - for (let item in test_servers) { - let re = RegExp('https?://'+test_servers[item].host); - if (re.test(uri)) { - return test_servers[item]; - } - } - return null; - }, - http_on_modify_request: function (subject, topic, data) { - let channel = subject.QueryInterface(Ci.nsIHttpChannel); - if (channel.requestMethod != 'HEAD') { - return; - } - - let curTest = this.get_current_test(channel.URI.asciiSpec); - - if (!curTest) { - return; - } - - ok(!(curTest.id in test_settings[which_test].priming), "Already saw a priming request for " + curTest.id); - test_settings[which_test].priming[curTest.id] = true; - }, - // When we see a response come back, peek at the response and test it is secure - // or insecure as needed. Addtionally, watch the response for priming requests. - http_on_examine_response: function (subject, topic, data) { - let channel = subject.QueryInterface(Ci.nsIHttpChannel); - let curTest = this.get_current_test(channel.URI.asciiSpec); - - if (!curTest) { - return; - } - - let result = (channel.URI.asciiSpec.startsWith('https:')) ? "secure" : "insecure"; - - // This is a priming request, go ahead and validate we were supposed to see - // a response from the server - if (channel.requestMethod == 'HEAD') { - is(true, curTest.response, "HSTS priming response found " + curTest.id); - return; - } - - // This is the response to our query, make sure it matches - is(result, test_settings[which_test].result[curTest.id], - "HSTS priming result " + which_test + ":" + curTest.id); - test_settings[which_test].finished[curTest.id] = result; - }, -}; - -// opens `uri' in a new tab and focuses it. -// returns the newly opened tab -function openTab(uri) { - let tab = gBrowser.addTab(uri); - - // select tab and make sure its browser is focused - gBrowser.selectedTab = tab; - tab.ownerDocument.defaultView.focus(); - - return tab; -} - -function clear_sts_data() { - for (let test in test_servers) { - SpecialPowers.cleanUpSTSData('http://'+test_servers[test].host); - } -} - -function do_cleanup() { - clear_sts_data(); - - Services.obs.removeObserver(Observer, "console-api-log-event"); - Services.obs.removeObserver(Observer, "http-on-examine-response"); -} - -function SetupPrefTestEnvironment(which, additional_prefs) { - which_test = which; - clear_sts_data(); - - var settings = test_settings[which]; - // priming counts how many priming requests we saw - settings.priming = {}; - // priming counts how many tests were finished - settings.finished= {}; - - var prefs = [["security.mixed_content.block_active_content", - settings.block_active], - ["security.mixed_content.block_display_content", - settings.block_display], - ["security.mixed_content.use_hsts", - settings.use_hsts], - ["security.mixed_content.send_hsts_priming", - settings.send_hsts_priming]]; - - if (additional_prefs) { - for (let idx in additional_prefs) { - prefs.push(additional_prefs[idx]); - } - } - - console.log("prefs=%s", prefs); - - SpecialPowers.pushPrefEnv({'set': prefs}); -} - -// make the top-level test uri -function build_test_uri(base_uri, host, test_id, type) { - return base_uri + - "?host=" + escape(host) + - "&id=" + escape(test_id) + - "&type=" + escape(type); -} - -// open a new tab, load the test, and wait for it to finish -function execute_test(test, mimetype) { - var src = build_test_uri(TOP_URI, test_servers[test].host, - test, test_settings[which_test].type); - - let tab = openTab(src); - test_servers[test]['tab'] = tab; - - let browser = gBrowser.getBrowserForTab(tab); - yield BrowserTestUtils.browserLoaded(browser); - - yield BrowserTestUtils.removeTab(tab); -} diff --git a/dom/security/test/mixedcontentblocker/test_main.html b/dom/security/test/mixedcontentblocker/test_main.html index d2bc9dc7e..bb9536939 100644 --- a/dom/security/test/mixedcontentblocker/test_main.html +++ b/dom/security/test/mixedcontentblocker/test_main.html @@ -162,9 +162,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178 } function startTest() { - // Set prefs to use mixed-content before HSTS - SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.use_hsts", false], - ["security.mixed_content.send_hsts_priming", false]]}); //Set the first set of mixed content settings and increment the counter. changePrefs([], function() { //listen for a messages from the mixed content test harness diff --git a/dom/security/test/moz.build b/dom/security/test/moz.build index ddb4e9b89..759e76c73 100644 --- a/dom/security/test/moz.build +++ b/dom/security/test/moz.build @@ -27,5 +27,5 @@ MOCHITEST_CHROME_MANIFESTS += [ BROWSER_CHROME_MANIFESTS += [ 'contentverifier/browser.ini', 'csp/browser.ini', - 'hsts/browser.ini', + 'general/browser.ini', ] |