summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoonchild <mcwerewolf@gmail.com>2018-04-23 11:46:21 +0200
committerGitHub <noreply@github.com>2018-04-23 11:46:21 +0200
commit8ed46f424e1a8a09bad7147882b83c9b2aad17c6 (patch)
tree5c6953fd44fddb74891ddd96613f1ef949c85fd7
parent8ffac11aa6eb32be75ff049787191e12476586d3 (diff)
parentccbd5ecf57fcd53ac8b28ddf7466b6c930f764df (diff)
downloaduxp-8ed46f424e1a8a09bad7147882b83c9b2aad17c6.tar.gz
Merge pull request #231 from janekptacijarabaci/security_blocking_data_1
moebius#223, #224, #226, #230: DOM - consider blocking top level window data: URIs
-rw-r--r--application/palemoon/base/content/nsContextMenu.js3
-rw-r--r--application/palemoon/base/content/utilityOverlay.js5
-rw-r--r--browser/base/content/nsContextMenu.js3
-rw-r--r--browser/base/content/utilityOverlay.js5
-rw-r--r--caps/nsNullPrincipalURI.cpp26
-rw-r--r--docshell/base/nsDSURIContentListener.cpp9
-rw-r--r--docshell/base/nsDocShell.cpp111
-rw-r--r--docshell/base/nsDocShell.h2
-rw-r--r--docshell/base/nsDocShellLoadInfo.cpp15
-rw-r--r--docshell/base/nsDocShellLoadInfo.h1
-rw-r--r--docshell/base/nsIDocShell.idl3
-rw-r--r--docshell/base/nsIDocShellLoadInfo.idl6
-rw-r--r--docshell/base/nsIWebNavigation.idl6
-rw-r--r--docshell/test/navigation/file_contentpolicy_block_window.html5
-rw-r--r--docshell/test/navigation/mochitest.ini2
-rw-r--r--docshell/test/navigation/test_contentpolicy_block_window.html96
-rw-r--r--dom/base/Location.cpp21
-rw-r--r--dom/base/nsContentPolicy.cpp6
-rw-r--r--dom/locales/en-US/chrome/security/security.properties3
-rw-r--r--dom/security/nsCSPContext.cpp5
-rw-r--r--dom/security/nsContentSecurityManager.cpp140
-rw-r--r--dom/security/nsContentSecurityManager.h2
-rw-r--r--dom/security/test/general/browser.ini14
-rw-r--r--dom/security/test/general/browser_test_data_download.js37
-rw-r--r--dom/security/test/general/browser_test_data_text_csv.js37
-rw-r--r--dom/security/test/general/browser_test_toplevel_data_navigations.js54
-rw-r--r--dom/security/test/general/browser_test_view_image_data_navigation.js30
-rw-r--r--dom/security/test/general/file_block_toplevel_data_navigation.html14
-rw-r--r--dom/security/test/general/file_block_toplevel_data_navigation2.html29
-rw-r--r--dom/security/test/general/file_block_toplevel_data_navigation3.html13
-rw-r--r--dom/security/test/general/file_block_toplevel_data_redirect.sjs14
-rw-r--r--dom/security/test/general/file_data_download.html14
-rw-r--r--dom/security/test/general/file_data_text_csv.html14
-rw-r--r--dom/security/test/general/file_toplevel_data_meta_redirect.html10
-rw-r--r--dom/security/test/general/file_toplevel_data_navigations.sjs14
-rw-r--r--dom/security/test/general/file_view_image_data_navigation.html12
-rw-r--r--dom/security/test/general/mochitest.ini12
-rw-r--r--dom/security/test/general/test_allow_opening_data_json.html39
-rw-r--r--dom/security/test/general/test_allow_opening_data_pdf.html41
-rw-r--r--dom/security/test/general/test_block_toplevel_data_img_navigation.html53
-rw-r--r--dom/security/test/general/test_block_toplevel_data_navigation.html86
-rw-r--r--dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html1
-rw-r--r--dom/security/test/moz.build1
-rw-r--r--dom/url/URL.cpp40
-rw-r--r--image/decoders/icon/nsIconURI.cpp26
-rw-r--r--ipc/glue/BackgroundUtils.cpp4
-rw-r--r--modules/libjar/nsJARURI.cpp1
-rw-r--r--modules/libjar/nsJARURI.h1
-rw-r--r--modules/libpref/init/all.js6
-rw-r--r--netwerk/base/LoadInfo.cpp60
-rw-r--r--netwerk/base/LoadInfo.h11
-rw-r--r--netwerk/base/moz.build1
-rw-r--r--netwerk/base/nsILoadInfo.idl22
-rw-r--r--netwerk/base/nsIOService.cpp10
-rw-r--r--netwerk/base/nsIOService.h4
-rw-r--r--netwerk/base/nsIURI.idl60
-rw-r--r--netwerk/base/nsIURIWithQuery.idl30
-rw-r--r--netwerk/base/nsIURL.idl6
-rw-r--r--netwerk/base/nsSimpleURI.cpp6
-rw-r--r--netwerk/base/nsSimpleURI.h4
-rw-r--r--netwerk/base/nsStandardURL.cpp1
-rw-r--r--netwerk/base/nsStandardURL.h1
-rw-r--r--netwerk/ipc/NeckoChannelParams.ipdlh2
-rw-r--r--toolkit/modules/addons/WebRequestContent.js10
64 files changed, 1149 insertions, 171 deletions
diff --git a/application/palemoon/base/content/nsContextMenu.js b/application/palemoon/base/content/nsContextMenu.js
index 3d5d40e4c6..830c209986 100644
--- a/application/palemoon/base/content/nsContextMenu.js
+++ b/application/palemoon/base/content/nsContextMenu.js
@@ -909,7 +909,8 @@ nsContextMenu.prototype = {
Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
let doc = this.target.ownerDocument;
openUILink(viewURL, e, { disallowInheritPrincipal: true,
- referrerURI: doc.documentURIObject });
+ referrerURI: doc.documentURIObject,
+ forceAllowDataURI: true });
}
},
diff --git a/application/palemoon/base/content/utilityOverlay.js b/application/palemoon/base/content/utilityOverlay.js
index b1e78d6a95..86cc5cea5a 100644
--- a/application/palemoon/base/content/utilityOverlay.js
+++ b/application/palemoon/base/content/utilityOverlay.js
@@ -205,6 +205,7 @@ function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI
openLinkIn(url, where, params);
}
+/* eslint-disable complexity */
function openLinkIn(url, where, params) {
if (!where || !url)
return;
@@ -215,6 +216,7 @@ function openLinkIn(url, where, params) {
var aCharset = params.charset;
var aReferrerURI = params.referrerURI;
var aRelatedToCurrent = params.relatedToCurrent;
+ var aForceAllowDataURI = params.forceAllowDataURI;
var aInBackground = params.inBackground;
var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
var aInitiatingDoc = params.initiatingDoc;
@@ -315,6 +317,9 @@ function openLinkIn(url, where, params) {
}
if (aDisallowInheritPrincipal)
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
+ if (aForceAllowDataURI) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
+ }
w.gBrowser.loadURIWithFlags(url, flags, aReferrerURI, null, aPostData);
break;
case "tabshifted":
diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js
index 8eb9b034f6..ddf6952026 100644
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1158,7 +1158,8 @@ nsContextMenu.prototype = {
this.browser.contentPrincipal,
Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
openUILink(this.mediaURL, e, { disallowInheritPrincipal: true,
- referrerURI: referrerURI });
+ referrerURI: referrerURI,
+ forceAllowDataURI: true });
}
},
diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js
index 0b703b6f84..6ceaf773ea 100644
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -197,6 +197,7 @@ function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI
openLinkIn(url, where, params);
}
+/* eslint-disable complexity */
function openLinkIn(url, where, params) {
if (!where || !url)
return;
@@ -212,6 +213,7 @@ function openLinkIn(url, where, params) {
params.referrerPolicy : Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT);
var aRelatedToCurrent = params.relatedToCurrent;
var aAllowMixedContent = params.allowMixedContent;
+ var aForceAllowDataURI = params.forceAllowDataURI;
var aInBackground = params.inBackground;
var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
var aInitiatingDoc = params.initiatingDoc;
@@ -378,6 +380,9 @@ function openLinkIn(url, where, params) {
if (aIndicateErrorPageLoad) {
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ERROR_LOAD_CHANGES_RV;
}
+ if (aForceAllowDataURI) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
+ }
let {URI_INHERITS_SECURITY_CONTEXT} = Ci.nsIProtocolHandler;
if (aForceAboutBlankViewerInCurrent &&
diff --git a/caps/nsNullPrincipalURI.cpp b/caps/nsNullPrincipalURI.cpp
index 891a29bd80..f8b8671606 100644
--- a/caps/nsNullPrincipalURI.cpp
+++ b/caps/nsNullPrincipalURI.cpp
@@ -168,6 +168,32 @@ nsNullPrincipalURI::SetPath(const nsACString &aPath)
}
NS_IMETHODIMP
+nsNullPrincipalURI::GetFilePath(nsACString &aFilePath)
+{
+ aFilePath.Truncate();
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetFilePath(const nsACString &aFilePath)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetQuery(nsACString &aQuery)
+{
+ aQuery.Truncate();
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetQuery(const nsACString &aQuery)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
nsNullPrincipalURI::GetRef(nsACString &_ref)
{
_ref.Truncate();
diff --git a/docshell/base/nsDSURIContentListener.cpp b/docshell/base/nsDSURIContentListener.cpp
index 93ce3cb264..ee6a4dd623 100644
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -17,6 +17,7 @@
#include "nsIHttpChannel.h"
#include "nsIScriptSecurityManager.h"
#include "nsError.h"
+#include "nsContentSecurityManager.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
@@ -93,6 +94,14 @@ nsDSURIContentListener::DoContent(const nsACString& aContentType,
if (aOpenedChannel) {
aOpenedChannel->GetLoadFlags(&loadFlags);
+
+ // block top-level data URI navigations if triggered by the web
+ if (!nsContentSecurityManager::AllowTopLevelNavigationToDataURI(aOpenedChannel)) {
+ // logging to console happens within AllowTopLevelNavigationToDataURI
+ aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
+ *aAbortProcess = true;
+ return NS_OK;
+ }
}
if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) {
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 58c182cbb5..bd2a8a4338 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -42,6 +42,7 @@
#include "nsArray.h"
#include "nsArrayUtils.h"
+#include "nsContentSecurityManager.h"
#include "nsICaptivePortalService.h"
#include "nsIDOMStorage.h"
#include "nsIContentViewer.h"
@@ -138,6 +139,7 @@
#include "nsISiteSecurityService.h"
#include "nsStructuredCloneContainer.h"
#include "nsIStructuredCloneContainer.h"
+#include "nsISupportsPrimitives.h"
#ifdef MOZ_PLACES
#include "nsIFaviconService.h"
#include "mozIPlacesPendingOperation.h"
@@ -1272,6 +1274,7 @@ nsDocShell::LoadURI(nsIURI* aURI,
nsCOMPtr<nsISHEntry> shEntry;
nsXPIDLString target;
nsAutoString srcdoc;
+ bool forceAllowDataURI = false;
nsCOMPtr<nsIDocShell> sourceDocShell;
nsCOMPtr<nsIURI> baseURI;
@@ -1307,6 +1310,7 @@ nsDocShell::LoadURI(nsIURI* aURI,
aLoadInfo->GetSrcdocData(srcdoc);
aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
+ aLoadInfo->GetForceAllowDataURI(&forceAllowDataURI);
}
#if defined(DEBUG)
@@ -1560,6 +1564,10 @@ nsDocShell::LoadURI(nsIURI* aURI,
flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
}
+ if (forceAllowDataURI) {
+ flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
+ }
+
return InternalLoad(aURI,
originalURI,
loadReplace,
@@ -4821,6 +4829,9 @@ nsDocShell::LoadURIWithOptions(const char16_t* aURI,
}
nsAutoPopupStatePusher statePusher(popupState);
+ bool forceAllowDataURI =
+ aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
+
// Don't pass certain flags that aren't needed and end up confusing
// ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are
// passed to LoadURI though, since it uses them.
@@ -4850,6 +4861,7 @@ nsDocShell::LoadURIWithOptions(const char16_t* aURI,
loadInfo->SetReferrerPolicy(aReferrerPolicy);
loadInfo->SetHeadersStream(aHeaderStream);
loadInfo->SetBaseURI(aBaseURI);
+ loadInfo->SetForceAllowDataURI(forceAllowDataURI);
if (fixupInfo) {
nsAutoString searchProvider, keyword;
@@ -9884,49 +9896,49 @@ nsDocShell::InternalLoad(nsIURI* aURI,
contentType = nsIContentPolicy::TYPE_DOCUMENT;
}
- // If there's no targetDocShell, that means we are about to create a new window,
- // perform a content policy check before creating the window.
- if (!targetDocShell) {
- nsCOMPtr<Element> requestingElement;
+ // If there's no targetDocShell, that means we are about to create a new
+ // window (or aWindowTarget is empty). Perform a content policy check before
+ // creating the window. Please note for all other docshell loads
+ // content policy checks are performed within the contentSecurityManager
+ // when the channel is about to be openend.
+ if (!targetDocShell && !aWindowTarget.IsEmpty()) {
+ MOZ_ASSERT(contentType == nsIContentPolicy::TYPE_DOCUMENT,
+ "opening a new window requires type to be TYPE_DOCUMENT");
+
nsISupports* requestingContext = nullptr;
- if (contentType == nsIContentPolicy::TYPE_DOCUMENT) {
- if (XRE_IsContentProcess()) {
- // In e10s the child process doesn't have access to the element that
- // contains the browsing context (because that element is in the chrome
- // process). So we just pass mScriptGlobal.
- requestingContext = ToSupports(mScriptGlobal);
- } else {
- // This is for loading non-e10s tabs and toplevel windows of various
- // sorts.
- // For the toplevel window cases, requestingElement will be null.
- requestingElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
- requestingContext = requestingElement;
- }
+ if (XRE_IsContentProcess()) {
+ // In e10s the child process doesn't have access to the element that
+ // contains the browsing context (because that element is in the chrome
+ // process). So we just pass mScriptGlobal.
+ requestingContext = ToSupports(mScriptGlobal);
} else {
- requestingElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
+ // This is for loading non-e10s tabs and toplevel windows of various
+ // sorts.
+ // For the toplevel window cases, requestingElement will be null.
+ nsCOMPtr<Element> requestingElement =
+ mScriptGlobal->AsOuter()->GetFrameElementInternal();
requestingContext = requestingElement;
-
-#ifdef DEBUG
- if (requestingElement) {
- // Get the docshell type for requestingElement.
- nsCOMPtr<nsIDocument> requestingDoc = requestingElement->OwnerDoc();
- nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
-
- // requestingElement docshell type = current docshell type.
- MOZ_ASSERT(mItemType == elementDocShell->ItemType(),
- "subframes should have the same docshell type as their parent");
- }
-#endif
}
+ // Since Content Policy checks are performed within docShell as well as
+ // the ContentSecurityManager we need a reliable way to let certain
+ // nsIContentPolicy consumers ignore duplicate calls. Let's use the 'extra'
+ // argument to pass a specific identifier.
+ nsCOMPtr<nsISupportsString> extraStr =
+ do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_NAMED_LITERAL_STRING(msg, "conPolCheckFromDocShell");
+ rv = extraStr->SetData(msg);
+ NS_ENSURE_SUCCESS(rv, rv);
+
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(contentType,
aURI,
aTriggeringPrincipal,
requestingContext,
EmptyCString(), // mime guess
- nullptr, // extra
+ extraStr, // extra
&shouldLoad);
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
@@ -10082,6 +10094,7 @@ nsDocShell::InternalLoad(nsIURI* aURI,
// principal to inherit is: it should be aTriggeringPrincipal.
loadInfo->SetPrincipalIsExplicit(true);
loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(LOAD_LINK));
+ loadInfo->SetForceAllowDataURI(aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI);
rv = win->Open(NS_ConvertUTF8toUTF16(spec),
aWindowTarget, // window name
@@ -10232,8 +10245,11 @@ nsDocShell::InternalLoad(nsIURI* aURI,
}
}
+ bool loadFromExternal = false;
+
// Before going any further vet loads initiated by external programs.
if (aLoadType == LOAD_NORMAL_EXTERNAL) {
+ loadFromExternal = true;
// Disallow external chrome: loads targetted at content windows
bool isChrome = false;
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
@@ -10724,7 +10740,9 @@ nsDocShell::InternalLoad(nsIURI* aURI,
nsINetworkPredictor::PREDICT_LOAD, this, nullptr);
nsCOMPtr<nsIRequest> req;
- rv = DoURILoad(aURI, aOriginalURI, aLoadReplace, aReferrer,
+ rv = DoURILoad(aURI, aOriginalURI, aLoadReplace, loadFromExternal,
+ (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI),
+ aReferrer,
!(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
aReferrerPolicy,
aTriggeringPrincipal, principalToInherit, aTypeHint,
@@ -10804,6 +10822,8 @@ nsresult
nsDocShell::DoURILoad(nsIURI* aURI,
nsIURI* aOriginalURI,
bool aLoadReplace,
+ bool aLoadFromExternal,
+ bool aForceAllowDataURI,
nsIURI* aReferrerURI,
bool aSendReferrer,
uint32_t aReferrerPolicy,
@@ -10880,17 +10900,40 @@ nsDocShell::DoURILoad(nsIURI* aURI,
nsCOMPtr<nsINode> loadingNode;
nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
nsCOMPtr<nsIPrincipal> loadingPrincipal;
+ nsCOMPtr<nsISupports> topLevelLoadingContext;
if (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
loadingNode = nullptr;
loadingPrincipal = nullptr;
loadingWindow = mScriptGlobal->AsOuter();
+ if (XRE_IsContentProcess()) {
+ // In e10s the child process doesn't have access to the element that
+ // contains the browsing context (because that element is in the chrome
+ // process).
+ nsCOMPtr<nsITabChild> tabChild = GetTabChild();
+ topLevelLoadingContext = ToSupports(tabChild);
+ } else {
+ // This is for loading non-e10s tabs and toplevel windows of various
+ // sorts.
+ // For the toplevel window cases, requestingElement will be null.
+ nsCOMPtr<Element> requestingElement =
+ loadingWindow->GetFrameElementInternal();
+ topLevelLoadingContext = requestingElement;
+ }
} else {
loadingWindow = nullptr;
loadingNode = mScriptGlobal->AsOuter()->GetFrameElementInternal();
if (loadingNode) {
// If we have a loading node, then use that as our loadingPrincipal.
loadingPrincipal = loadingNode->NodePrincipal();
+#ifdef DEBUG
+ // Get the docshell type for requestingElement.
+ nsCOMPtr<nsIDocument> requestingDoc = loadingNode->OwnerDoc();
+ nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
+ // requestingElement docshell type = current docshell type.
+ MOZ_ASSERT(mItemType == elementDocShell->ItemType(),
+ "subframes should have the same docshell type as their parent");
+#endif
} else {
// If this isn't a top-level load and mScriptGlobal's frame element is
// null, then the element got removed from the DOM while we were trying
@@ -10940,7 +10983,7 @@ nsDocShell::DoURILoad(nsIURI* aURI,
nsCOMPtr<nsILoadInfo> loadInfo =
(aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) ?
- new LoadInfo(loadingWindow, aTriggeringPrincipal,
+ new LoadInfo(loadingWindow, aTriggeringPrincipal, topLevelLoadingContext,
securityFlags) :
new LoadInfo(loadingPrincipal, aTriggeringPrincipal, loadingNode,
securityFlags, aContentPolicyType);
@@ -10948,6 +10991,8 @@ nsDocShell::DoURILoad(nsIURI* aURI,
if (aPrincipalToInherit) {
loadInfo->SetPrincipalToInherit(aPrincipalToInherit);
}
+ loadInfo->SetLoadTriggeredFromExternal(aLoadFromExternal);
+ loadInfo->SetForceAllowDataURI(aForceAllowDataURI);
// We have to do this in case our OriginAttributes are different from the
// OriginAttributes of the parent document. Or in case there isn't a
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index 3ca9e0b344..63a4e3358e 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -369,6 +369,8 @@ protected:
nsresult DoURILoad(nsIURI* aURI,
nsIURI* aOriginalURI,
bool aLoadReplace,
+ bool aLoadFromExternal,
+ bool aForceAllowDataURI,
nsIURI* aReferrer,
bool aSendReferrer,
uint32_t aReferrerPolicy,
diff --git a/docshell/base/nsDocShellLoadInfo.cpp b/docshell/base/nsDocShellLoadInfo.cpp
index 7d0034b040..b00e8e3603 100644
--- a/docshell/base/nsDocShellLoadInfo.cpp
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -15,6 +15,7 @@ nsDocShellLoadInfo::nsDocShellLoadInfo()
: mLoadReplace(false)
, mInheritPrincipal(false)
, mPrincipalIsExplicit(false)
+ , mForceAllowDataURI(false)
, mSendReferrer(true)
, mReferrerPolicy(mozilla::net::RP_Default)
, mLoadType(nsIDocShellLoadInfo::loadNormal)
@@ -127,6 +128,20 @@ nsDocShellLoadInfo::SetPrincipalIsExplicit(bool aPrincipalIsExplicit)
}
NS_IMETHODIMP
+nsDocShellLoadInfo::GetForceAllowDataURI(bool* aForceAllowDataURI)
+{
+ *aForceAllowDataURI = mForceAllowDataURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellLoadInfo::SetForceAllowDataURI(bool aForceAllowDataURI)
+{
+ mForceAllowDataURI = aForceAllowDataURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsDocShellLoadInfo::GetLoadType(nsDocShellInfoLoadType* aLoadType)
{
NS_ENSURE_ARG_POINTER(aLoadType);
diff --git a/docshell/base/nsDocShellLoadInfo.h b/docshell/base/nsDocShellLoadInfo.h
index b7eaed832a..f3ddcca1e6 100644
--- a/docshell/base/nsDocShellLoadInfo.h
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -37,6 +37,7 @@ protected:
bool mLoadReplace;
bool mInheritPrincipal;
bool mPrincipalIsExplicit;
+ bool mForceAllowDataURI;
bool mSendReferrer;
nsDocShellInfoReferrerPolicy mReferrerPolicy;
nsDocShellInfoLoadType mLoadType;
diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
index 8261c45dcb..e34e6adfdf 100644
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -116,6 +116,9 @@ interface nsIDocShell : nsIDocShellTreeItem
const long INTERNAL_LOAD_FLAGS_NO_OPENER = 0x100;
+ // Whether a top-level data URI navigation is allowed for that load
+ const long INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI = 0x200;
+
// NB: 0x80 is available.
/**
diff --git a/docshell/base/nsIDocShellLoadInfo.idl b/docshell/base/nsIDocShellLoadInfo.idl
index 113c0a4c17..8804f63a3c 100644
--- a/docshell/base/nsIDocShellLoadInfo.idl
+++ b/docshell/base/nsIDocShellLoadInfo.idl
@@ -55,6 +55,12 @@ interface nsIDocShellLoadInfo : nsISupports
*/
attribute boolean principalIsExplicit;
+ /**
+ * If this attribute is true, then a top-level navigation
+ * to a data URI will be allowed.
+ */
+ attribute boolean forceAllowDataURI;
+
/* these are load type enums... */
const long loadNormal = 0; // Normal Load
const long loadNormalReplace = 1; // Normal Load but replaces current history slot
diff --git a/docshell/base/nsIWebNavigation.idl b/docshell/base/nsIWebNavigation.idl
index 042b1c5479..241d0731c1 100644
--- a/docshell/base/nsIWebNavigation.idl
+++ b/docshell/base/nsIWebNavigation.idl
@@ -206,6 +206,12 @@ interface nsIWebNavigation : nsISupports
const unsigned long LOAD_FLAGS_FIXUP_SCHEME_TYPOS = 0x200000;
/**
+ * Allows a top-level data: navigation to occur. E.g. view-image
+ * is an explicit user action which should be allowed.
+ */
+ const unsigned long LOAD_FLAGS_FORCE_ALLOW_DATA_URI = 0x400000;
+
+ /**
* Loads a given URI. This will give priority to loading the requested URI
* in the object implementing this interface. If it can't be loaded here
* however, the URI dispatcher will go through its normal process of content
diff --git a/docshell/test/navigation/file_contentpolicy_block_window.html b/docshell/test/navigation/file_contentpolicy_block_window.html
new file mode 100644
index 0000000000..c51e574e5e
--- /dev/null
+++ b/docshell/test/navigation/file_contentpolicy_block_window.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+This window should never be openend!
+</body>
+</html>
diff --git a/docshell/test/navigation/mochitest.ini b/docshell/test/navigation/mochitest.ini
index 0c35cf352c..764e400a8f 100644
--- a/docshell/test/navigation/mochitest.ini
+++ b/docshell/test/navigation/mochitest.ini
@@ -36,6 +36,7 @@ support-files =
file_bug1300461_redirect.html
file_bug1300461_redirect.html^headers^
file_bug1300461_back.html
+ file_contentpolicy_block_window.html
[test_bug13871.html]
[test_bug270414.html]
@@ -62,3 +63,4 @@ skip-if = toolkit == 'android' #RANDOM
[test_triggeringprincipal_window_open.html]
[test_triggeringprincipal_parent_iframe_window_open.html]
[test_triggeringprincipal_iframe_iframe_window_open.html]
+[test_contentpolicy_block_window.html]
diff --git a/docshell/test/navigation/test_contentpolicy_block_window.html b/docshell/test/navigation/test_contentpolicy_block_window.html
new file mode 100644
index 0000000000..651be825cf
--- /dev/null
+++ b/docshell/test/navigation/test_contentpolicy_block_window.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1329288
+-->
+<head>
+ <title>Test for Bug 1329288</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1329288">Mozilla Bug 1329288</a>
+
+
+<!-- have a testlink which we can use for the test to open a new window -->
+<a href="http://test1.example.org/tests/docshell/test/navigation/file_contentpolicy_block_window.html"
+ target="_blank"
+ id="testlink">This is a link</a>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * The test tries to open a new window and makes sure that a registered contentPolicy
+ * gets called with the right (a non null) 'context' for the TYPE_DOCUMENT load.
+ */
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+var componentManager = SpecialPowers.wrap(SpecialPowers.Components).manager
+ .QueryInterface(Ci.nsIComponentRegistrar);
+
+// Content policy / factory implementation for the test
+var policyID = SpecialPowers.wrap(SpecialPowers.Components).ID("{b80e19d0-878f-d41b-2654-194714a4115c}");
+var policyName = "@mozilla.org/testpolicy;1";
+var policy = {
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ iid = SpecialPowers.wrap(iid);
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIContentPolicy))
+ return this;
+ throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance: function(outer, iid) {
+ return this.QueryInterface(iid);
+ },
+
+ // nsIContentPolicy implementation
+ shouldLoad: function(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {
+
+ if (SpecialPowers.wrap(contentLocation).spec !== document.getElementById("testlink").href) {
+ // not the URI we are looking for, allow the load
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+
+ is(contentType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ "needs to be type document load");
+ ok(context, "context is not allowed to be null");
+ ok(context.name.endsWith("test_contentpolicy_block_window.html"),
+ "context should be the current window");
+
+ // remove the policy and finish test.
+ categoryManager.deleteCategoryEntry("content-policy", policyName, false);
+
+ setTimeout(function() {
+ // Component must be unregistered delayed, otherwise other content
+ // policy will not be removed from the category correctly
+ componentManager.unregisterFactory(policyID, policy);
+ }, 0);
+
+ SimpleTest.finish();
+ return Ci.nsIContentPolicy.REJECT_REQUEST;
+ },
+
+ shouldProcess: function(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+}
+
+policy = SpecialPowers.wrapCallbackObject(policy);
+componentManager.registerFactory(policyID, "Test content policy", policyName, policy);
+categoryManager.addCategoryEntry("content-policy", policyName, policyName, false, true);
+
+SimpleTest.waitForExplicitFinish();
+
+// now everything is set up, let's start the test
+document.getElementById("testlink").click()
+
+</script>
+</body>
+</html>
diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp
index e3b614931a..b6b95aaa6e 100644
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -577,19 +577,17 @@ Location::GetPathname(nsAString& aPathname)
aPathname.Truncate();
nsCOMPtr<nsIURI> uri;
- nsresult result = NS_OK;
+ nsresult result = GetURI(getter_AddRefs(uri));
+ if (NS_FAILED(result) || !uri) {
+ return result;
+ }
- result = GetURI(getter_AddRefs(uri));
+ nsAutoCString file;
- nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(uri));
- if (url) {
- nsAutoCString file;
+ result = uri->GetFilePath(file);
- result = url->GetFilePath(file);
-
- if (NS_SUCCEEDED(result)) {
- AppendUTF8toUTF16(file, aPathname);
- }
+ if (NS_SUCCEEDED(result)) {
+ AppendUTF8toUTF16(file, aPathname);
}
return result;
@@ -604,8 +602,7 @@ Location::SetPathname(const nsAString& aPathname)
return rv;
}
- nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(uri));
- if (url && NS_SUCCEEDED(url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname)))) {
+ if (NS_SUCCEEDED(uri->SetFilePath(NS_ConvertUTF16toUTF8(aPathname)))) {
return SetURI(uri);
}
diff --git a/dom/base/nsContentPolicy.cpp b/dom/base/nsContentPolicy.cpp
index 337debcea8..5511b90864 100644
--- a/dom/base/nsContentPolicy.cpp
+++ b/dom/base/nsContentPolicy.cpp
@@ -20,6 +20,7 @@
#include "nsIDOMElement.h"
#include "nsIDOMNode.h"
#include "nsIDOMWindow.h"
+#include "nsITabChild.h"
#include "nsIContent.h"
#include "nsILoadContext.h"
#include "nsCOMArray.h"
@@ -89,8 +90,9 @@ nsContentPolicy::CheckPolicy(CPMethod policyMethod,
{
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(requestingContext));
nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(requestingContext));
- NS_ASSERTION(!requestingContext || node || window,
- "Context should be a DOM node or a DOM window!");
+ nsCOMPtr<nsITabChild> tabChild(do_QueryInterface(requestingContext));
+ NS_ASSERTION(!requestingContext || node || window || tabChild,
+ "Context should be a DOM node, DOM window or a tabChild!");
}
#endif
diff --git a/dom/locales/en-US/chrome/security/security.properties b/dom/locales/en-US/chrome/security/security.properties
index 8b66cc265e..c0b80996cc 100644
--- a/dom/locales/en-US/chrome/security/security.properties
+++ b/dom/locales/en-US/chrome/security/security.properties
@@ -81,3 +81,6 @@ MimeTypeMismatch=The resource from “%1$S” was blocked due to MIME type misma
XCTOHeaderValueMissing=X-Content-Type-Options header warning: value was “%1$S”; did you mean to send “nosniff”?
BlockScriptWithWrongMimeType=Script from “%1$S” was blocked because of a disallowed MIME type.
+
+# LOCALIZATION NOTE: Do not translate "data: URI".
+BlockTopLevelDataURINavigation=Navigation to toplevel data: URI not allowed (Blocked loading of: “%1$S”)
diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp
index a7517f65e8..979bd915f2 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,
diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp
index c4e1ed8e18..0cc4933fe1 100644
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -1,8 +1,10 @@
#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"
@@ -10,11 +12,86 @@
#include "nsMixedContentBlocker.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)
{
@@ -176,7 +253,7 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
nsContentPolicyType internalContentPolicyType =
aLoadInfo->InternalContentPolicyType();
nsCString mimeTypeGuess;
- nsCOMPtr<nsINode> requestingContext = nullptr;
+ nsCOMPtr<nsISupports> requestingContext = nullptr;
#ifdef DEBUG
// Don't enforce TYPE_DOCUMENT assertions for loads
@@ -250,10 +327,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 +354,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 +391,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 +424,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;
}
diff --git a/dom/security/nsContentSecurityManager.h b/dom/security/nsContentSecurityManager.h
index 912c0e89f1..bab8477434 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/test/general/browser.ini b/dom/security/test/general/browser.ini
new file mode 100644
index 0000000000..b00baa95dc
--- /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 0000000000..1ee8d58449
--- /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 0000000000..c45e40cc2e
--- /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 0000000000..a13a6350e9
--- /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 0000000000..22de358940
--- /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 0000000000..5fbfdfdef4
--- /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 0000000000..e0308e1ae6
--- /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 0000000000..34aeddab36
--- /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 0000000000..64e294cab1
--- /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 0000000000..4cc92fe8f5
--- /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 0000000000..a9ac369d16
--- /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 0000000000..f4f5deb528
--- /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 0000000000..501b833e5d
--- /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 0000000000..a3f9acfb4d
--- /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 70c0c9fb60..f3bcca0727 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 0000000000..1530a24e84
--- /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 0000000000..6b51fe57b0
--- /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 0000000000..7f8dfc7480
--- /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 0000000000..cef232b65a
--- /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 7b1ab72dc4..3ef2438240 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/moz.build b/dom/security/test/moz.build
index ddb4e9b895..946959deea 100644
--- a/dom/security/test/moz.build
+++ b/dom/security/test/moz.build
@@ -27,5 +27,6 @@ MOCHITEST_CHROME_MANIFESTS += [
BROWSER_CHROME_MANIFESTS += [
'contentverifier/browser.ini',
'csp/browser.ini',
+ 'general/browser.ini',
'hsts/browser.ini',
]
diff --git a/dom/url/URL.cpp b/dom/url/URL.cpp
index 1f15e11514..c8724c3597 100644
--- a/dom/url/URL.cpp
+++ b/dom/url/URL.cpp
@@ -17,7 +17,6 @@
#include "nsEscape.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsIIOService.h"
-#include "nsIURIWithQuery.h"
#include "nsIURL.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
@@ -525,21 +524,10 @@ URLMainThread::GetPathname(nsAString& aPathname, ErrorResult& aRv) const
// Do not throw! Not having a valid URI or URL should result in an empty
// string.
- nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(mURI));
- if (url) {
- nsAutoCString file;
- nsresult rv = url->GetFilePath(file);
- if (NS_SUCCEEDED(rv)) {
- CopyUTF8toUTF16(file, aPathname);
- }
-
- return;
- }
-
- nsAutoCString path;
- nsresult rv = mURI->GetPath(path);
+ nsAutoCString file;
+ nsresult rv = mURI->GetFilePath(file);
if (NS_SUCCEEDED(rv)) {
- CopyUTF8toUTF16(path, aPathname);
+ CopyUTF8toUTF16(file, aPathname);
}
}
@@ -548,11 +536,7 @@ URLMainThread::SetPathname(const nsAString& aPathname, ErrorResult& aRv)
{
// Do not throw!
- nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(mURI));
- if (url) {
- url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
- return;
- }
+ mURI->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
}
void
@@ -566,13 +550,9 @@ URLMainThread::GetSearch(nsAString& aSearch, ErrorResult& aRv) const
nsAutoCString search;
nsresult rv;
- nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(mURI));
- if (url) {
- rv = url->GetQuery(search);
- if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
- CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
- }
- return;
+ rv = mURI->GetQuery(search);
+ if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
+ CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
}
}
@@ -603,11 +583,7 @@ URLMainThread::SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv)
{
// Ignore failures to be compatible with NS4.
- nsCOMPtr<nsIURIWithQuery> uriWithQuery(do_QueryInterface(mURI));
- if (uriWithQuery) {
- uriWithQuery->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
- return;
- }
+ mURI->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
}
} // anonymous namespace
diff --git a/image/decoders/icon/nsIconURI.cpp b/image/decoders/icon/nsIconURI.cpp
index 2c2788c8f1..632a733fe9 100644
--- a/image/decoders/icon/nsIconURI.cpp
+++ b/image/decoders/icon/nsIconURI.cpp
@@ -372,6 +372,32 @@ nsMozIconURI::SetPath(const nsACString& aPath)
}
NS_IMETHODIMP
+nsMozIconURI::GetFilePath(nsACString& aFilePath)
+{
+ aFilePath.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetFilePath(const nsACString& aFilePath)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetQuery(nsACString& aQuery)
+{
+ aQuery.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetQuery(const nsACString& aQuery)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
nsMozIconURI::GetRef(nsACString& aRef)
{
aRef.Truncate();
diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp
index b335f5c23d..e115fa74c7 100644
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -280,6 +280,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
aLoadInfo->GetUpgradeInsecureRequests(),
aLoadInfo->GetVerifySignedContent(),
aLoadInfo->GetEnforceSRI(),
+ aLoadInfo->GetForceAllowDataURI(),
aLoadInfo->GetForceInheritPrincipalDropped(),
aLoadInfo->GetInnerWindowID(),
aLoadInfo->GetOuterWindowID(),
@@ -294,6 +295,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
aLoadInfo->CorsUnsafeHeaders(),
aLoadInfo->GetForcePreflight(),
aLoadInfo->GetIsPreflight(),
+ aLoadInfo->GetLoadTriggeredFromExternal(),
aLoadInfo->GetForceHSTSPriming(),
aLoadInfo->GetMixedContentWouldBlock());
@@ -356,6 +358,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
loadInfoArgs.upgradeInsecureRequests(),
loadInfoArgs.verifySignedContent(),
loadInfoArgs.enforceSRI(),
+ loadInfoArgs.forceAllowDataURI(),
loadInfoArgs.forceInheritPrincipalDropped(),
loadInfoArgs.innerWindowID(),
loadInfoArgs.outerWindowID(),
@@ -370,6 +373,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
loadInfoArgs.corsUnsafeHeaders(),
loadInfoArgs.forcePreflight(),
loadInfoArgs.isPreflight(),
+ loadInfoArgs.loadTriggeredFromExternal(),
loadInfoArgs.forceHSTSPriming(),
loadInfoArgs.mixedContentWouldBlock()
);
diff --git a/modules/libjar/nsJARURI.cpp b/modules/libjar/nsJARURI.cpp
index e46e514671..d1e4b5a593 100644
--- a/modules/libjar/nsJARURI.cpp
+++ b/modules/libjar/nsJARURI.cpp
@@ -41,7 +41,6 @@ NS_IMPL_RELEASE(nsJARURI)
NS_INTERFACE_MAP_BEGIN(nsJARURI)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJARURI)
NS_INTERFACE_MAP_ENTRY(nsIURI)
- NS_INTERFACE_MAP_ENTRY(nsIURIWithQuery)
NS_INTERFACE_MAP_ENTRY(nsIURL)
NS_INTERFACE_MAP_ENTRY(nsIJARURI)
NS_INTERFACE_MAP_ENTRY(nsISerializable)
diff --git a/modules/libjar/nsJARURI.h b/modules/libjar/nsJARURI.h
index 31271e4ac1..d2608a5c6d 100644
--- a/modules/libjar/nsJARURI.h
+++ b/modules/libjar/nsJARURI.h
@@ -41,7 +41,6 @@ class nsJARURI final : public nsIJARURI,
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIURI
- NS_DECL_NSIURIWITHQUERY
NS_DECL_NSIURL
NS_DECL_NSIJARURI
NS_DECL_NSISERIALIZABLE
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index 182de4a110..ccc59269b4 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5564,6 +5564,12 @@ pref("security.mixed_content.use_hsts", true);
// Approximately 1 week default cache for HSTS priming failures
pref ("security.mixed_content.hsts_priming_cache_timeout", 10080);
+// TODO: Bug 1380959: Block toplevel data: URI navigations
+// If true, all toplevel data: URI navigations will be blocked.
+// Please note that manually entering a data: URI in the
+// URL-Bar will not be blocked when flipping this pref.
+pref("security.data_uri.block_toplevel_data_uri_navigations", false);
+
// Disable Storage api in release builds.
#ifdef NIGHTLY_BUILD
pref("dom.storageManager.enabled", true);
diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp
index 42fdea4a18..ede825b8f9 100644
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -7,6 +7,7 @@
#include "mozilla/LoadInfo.h"
#include "mozilla/Assertions.h"
+#include "mozilla/dom/TabChild.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozIThirdPartyUtil.h"
#include "nsFrameLoader.h"
@@ -47,12 +48,14 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
aTriggeringPrincipal : mLoadingPrincipal.get())
, mPrincipalToInherit(nullptr)
, mLoadingContext(do_GetWeakReference(aLoadingContext))
+ , mContextForTopLevelLoad(nullptr)
, mSecurityFlags(aSecurityFlags)
, mInternalContentPolicyType(aContentPolicyType)
, mTainting(LoadTainting::Basic)
, mUpgradeInsecureRequests(false)
, mVerifySignedContent(false)
, mEnforceSRI(false)
+ , mForceAllowDataURI(false)
, mForceInheritPrincipalDropped(false)
, mInnerWindowID(0)
, mOuterWindowID(0)
@@ -63,6 +66,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
, mIsThirdPartyContext(false)
, mForcePreflight(false)
, mIsPreflight(false)
+ , mLoadTriggeredFromExternal(false)
, mForceHSTSPriming(false)
, mMixedContentWouldBlock(false)
{
@@ -215,16 +219,19 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
*/
LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
nsIPrincipal* aTriggeringPrincipal,
+ nsISupports* aContextForTopLevelLoad,
nsSecurityFlags aSecurityFlags)
: mLoadingPrincipal(nullptr)
, mTriggeringPrincipal(aTriggeringPrincipal)
, mPrincipalToInherit(nullptr)
+ , mContextForTopLevelLoad(do_GetWeakReference(aContextForTopLevelLoad))
, mSecurityFlags(aSecurityFlags)
, mInternalContentPolicyType(nsIContentPolicy::TYPE_DOCUMENT)
, mTainting(LoadTainting::Basic)
, mUpgradeInsecureRequests(false)
, mVerifySignedContent(false)
, mEnforceSRI(false)
+ , mForceAllowDataURI(false)
, mForceInheritPrincipalDropped(false)
, mInnerWindowID(0)
, mOuterWindowID(0)
@@ -235,6 +242,7 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
, mIsThirdPartyContext(false) // NB: TYPE_DOCUMENT implies not third-party.
, mForcePreflight(false)
, mIsPreflight(false)
+ , mLoadTriggeredFromExternal(false)
, mForceHSTSPriming(false)
, mMixedContentWouldBlock(false)
{
@@ -276,12 +284,14 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
, mTriggeringPrincipal(rhs.mTriggeringPrincipal)
, mPrincipalToInherit(rhs.mPrincipalToInherit)
, mLoadingContext(rhs.mLoadingContext)
+ , mContextForTopLevelLoad(rhs.mContextForTopLevelLoad)
, mSecurityFlags(rhs.mSecurityFlags)
, mInternalContentPolicyType(rhs.mInternalContentPolicyType)
, mTainting(rhs.mTainting)
, mUpgradeInsecureRequests(rhs.mUpgradeInsecureRequests)
, mVerifySignedContent(rhs.mVerifySignedContent)
, mEnforceSRI(rhs.mEnforceSRI)
+ , mForceAllowDataURI(rhs.mForceAllowDataURI)
, mForceInheritPrincipalDropped(rhs.mForceInheritPrincipalDropped)
, mInnerWindowID(rhs.mInnerWindowID)
, mOuterWindowID(rhs.mOuterWindowID)
@@ -297,6 +307,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
, mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders)
, mForcePreflight(rhs.mForcePreflight)
, mIsPreflight(rhs.mIsPreflight)
+ , mLoadTriggeredFromExternal(rhs.mLoadTriggeredFromExternal)
, mForceHSTSPriming(rhs.mForceHSTSPriming)
, mMixedContentWouldBlock(rhs.mMixedContentWouldBlock)
{
@@ -311,6 +322,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
bool aUpgradeInsecureRequests,
bool aVerifySignedContent,
bool aEnforceSRI,
+ bool aForceAllowDataURI,
bool aForceInheritPrincipalDropped,
uint64_t aInnerWindowID,
uint64_t aOuterWindowID,
@@ -325,6 +337,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
const nsTArray<nsCString>& aCorsUnsafeHeaders,
bool aForcePreflight,
bool aIsPreflight,
+ bool aLoadTriggeredFromExternal,
bool aForceHSTSPriming,
bool aMixedContentWouldBlock)
: mLoadingPrincipal(aLoadingPrincipal)
@@ -336,6 +349,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
, mUpgradeInsecureRequests(aUpgradeInsecureRequests)
, mVerifySignedContent(aVerifySignedContent)
, mEnforceSRI(aEnforceSRI)
+ , mForceAllowDataURI(aForceAllowDataURI)
, mForceInheritPrincipalDropped(aForceInheritPrincipalDropped)
, mInnerWindowID(aInnerWindowID)
, mOuterWindowID(aOuterWindowID)
@@ -348,6 +362,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
, mCorsUnsafeHeaders(aCorsUnsafeHeaders)
, mForcePreflight(aForcePreflight)
, mIsPreflight(aIsPreflight)
+ , mLoadTriggeredFromExternal(aLoadTriggeredFromExternal)
, mForceHSTSPriming (aForceHSTSPriming)
, mMixedContentWouldBlock(aMixedContentWouldBlock)
{
@@ -477,6 +492,17 @@ LoadInfo::LoadingNode()
return node;
}
+nsISupports*
+LoadInfo::ContextForTopLevelLoad()
+{
+ // Most likely you want to query LoadingNode() instead of
+ // ContextForTopLevelLoad() if this assertion fires.
+ MOZ_ASSERT(mInternalContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
+ "should only query this context for top level document loads");
+ nsCOMPtr<nsISupports> context = do_QueryReferent(mContextForTopLevelLoad);
+ return context;
+}
+
NS_IMETHODIMP
LoadInfo::GetSecurityFlags(nsSecurityFlags* aResult)
{
@@ -648,6 +674,23 @@ LoadInfo::GetEnforceSRI(bool* aResult)
}
NS_IMETHODIMP
+LoadInfo::SetForceAllowDataURI(bool aForceAllowDataURI)
+{
+ MOZ_ASSERT(!mForceAllowDataURI ||
+ mInternalContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
+ "can only allow data URI navigation for TYPE_DOCUMENT");
+ mForceAllowDataURI = aForceAllowDataURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::GetForceAllowDataURI(bool* aForceAllowDataURI)
+{
+ *aForceAllowDataURI = mForceAllowDataURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
LoadInfo::GetForceInheritPrincipalDropped(bool* aResult)
{
*aResult = mForceInheritPrincipalDropped;
@@ -873,6 +916,23 @@ LoadInfo::GetIsPreflight(bool* aIsPreflight)
}
NS_IMETHODIMP
+LoadInfo::SetLoadTriggeredFromExternal(bool aLoadTriggeredFromExternal)
+{
+ MOZ_ASSERT(!aLoadTriggeredFromExternal ||
+ mInternalContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
+ "can only set load triggered from external for TYPE_DOCUMENT");
+ mLoadTriggeredFromExternal = aLoadTriggeredFromExternal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::GetLoadTriggeredFromExternal(bool* aLoadTriggeredFromExternal)
+{
+ *aLoadTriggeredFromExternal = mLoadTriggeredFromExternal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
LoadInfo::GetForceHSTSPriming(bool* aForceHSTSPriming)
{
*aForceHSTSPriming = mForceHSTSPriming;
diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h
index 3e1b92ff4c..0ae6061b3e 100644
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -59,10 +59,12 @@ public:
nsSecurityFlags aSecurityFlags,
nsContentPolicyType aContentPolicyType);
- // Constructor used for TYPE_DOCUMENT loads which have no reasonable
- // loadingNode or loadingPrincipal
+ // Constructor used for TYPE_DOCUMENT loads which have a different
+ // loadingContext than other loads. This ContextForTopLevelLoad is
+ // only used for content policy checks.
LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
nsIPrincipal* aTriggeringPrincipal,
+ nsISupports* aContextForTopLevelLoad,
nsSecurityFlags aSecurityFlags);
// create an exact copy of the loadinfo
@@ -94,6 +96,7 @@ private:
bool aUpgradeInsecureRequests,
bool aVerifySignedContent,
bool aEnforceSRI,
+ bool aForceAllowDataURI,
bool aForceInheritPrincipalDropped,
uint64_t aInnerWindowID,
uint64_t aOuterWindowID,
@@ -108,6 +111,7 @@ private:
const nsTArray<nsCString>& aUnsafeHeaders,
bool aForcePreflight,
bool aIsPreflight,
+ bool aLoadTriggeredFromExternal,
bool aForceHSTSPriming,
bool aMixedContentWouldBlock);
LoadInfo(const LoadInfo& rhs);
@@ -132,12 +136,14 @@ private:
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
nsWeakPtr mLoadingContext;
+ nsWeakPtr mContextForTopLevelLoad;
nsSecurityFlags mSecurityFlags;
nsContentPolicyType mInternalContentPolicyType;
LoadTainting mTainting;
bool mUpgradeInsecureRequests;
bool mVerifySignedContent;
bool mEnforceSRI;
+ bool mForceAllowDataURI;
bool mForceInheritPrincipalDropped;
uint64_t mInnerWindowID;
uint64_t mOuterWindowID;
@@ -152,6 +158,7 @@ private:
nsTArray<nsCString> mCorsUnsafeHeaders;
bool mForcePreflight;
bool mIsPreflight;
+ bool mLoadTriggeredFromExternal;
bool mForceHSTSPriming : 1;
bool mMixedContentWouldBlock : 1;
diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build
index 3b731db109..5de1eea81e 100644
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -132,7 +132,6 @@ XPIDL_SOURCES += [
'nsIURIClassifier.idl',
'nsIURIWithBlobImpl.idl',
'nsIURIWithPrincipal.idl',
- 'nsIURIWithQuery.idl',
'nsIURL.idl',
'nsIURLParser.idl',
'nsPILoadGroupInternal.idl',
diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl
index 78433c8b88..4ec29b9725 100644
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -324,6 +324,16 @@ interface nsILoadInfo : nsISupports
nsINode binaryLoadingNode();
/**
+ * A C++ friendly version of the loadingContext for toplevel loads.
+ * Most likely you want to query the ownerDocument or LoadingNode
+ * and not this context only available for TYPE_DOCUMENT loads.
+ * Please note that except for loads of TYPE_DOCUMENT, this
+ * ContextForTopLevelLoad will always return null.
+ */
+ [noscript, notxpcom, nostdcall, binaryname(ContextForTopLevelLoad)]
+ nsISupports binaryContextForTopLevelLoad();
+
+ /**
* The securityFlags of that channel.
*/
readonly attribute nsSecurityFlags securityFlags;
@@ -470,6 +480,11 @@ interface nsILoadInfo : nsISupports
[infallible] attribute boolean enforceSRI;
/**
+ * If true, toplevel data: URI navigation is allowed
+ */
+ [infallible] attribute boolean forceAllowDataURI;
+
+ /**
* The SEC_FORCE_INHERIT_PRINCIPAL flag may be dropped when a load info
* object is created. Specifically, it will be dropped if the SEC_SANDBOXED
* flag is also present. This flag is set if SEC_FORCE_INHERIT_PRINCIPAL was
@@ -575,6 +590,13 @@ interface nsILoadInfo : nsISupports
[infallible] attribute boolean initialSecurityCheckDone;
/**
+ * Returns true if the load was triggered from an external application
+ * (e.g. Thunderbird). Please note that this flag will only ever be true
+ * if the load is of TYPE_DOCUMENT.
+ */
+ [infallible] attribute boolean loadTriggeredFromExternal;
+
+ /**
* Whenever a channel gets redirected, append the principal of the
* channel [before the channels got redirected] to the loadinfo,
* so that at every point this array lets us reason about all the
diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp
index 0da79c18ae..8b7f31f99d 100644
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -173,6 +173,8 @@ uint32_t nsIOService::gDefaultSegmentCount = 24;
bool nsIOService::sTelemetryEnabled = false;
+bool nsIOService::sBlockToplevelDataUriNavigations = false;
+
////////////////////////////////////////////////////////////////////////////////
nsIOService::nsIOService()
@@ -251,6 +253,8 @@ nsIOService::Init()
NS_WARNING("failed to get observer service");
Preferences::AddBoolVarCache(&sTelemetryEnabled, "toolkit.telemetry.enabled", false);
+ Preferences::AddBoolVarCache(&sBlockToplevelDataUriNavigations,
+ "security.data_uri.block_toplevel_data_uri_navigations", false);
Preferences::AddBoolVarCache(&mOfflineMirrorsConnectivity, OFFLINE_MIRRORS_CONNECTIVITY, true);
gIOService = this;
@@ -1876,5 +1880,11 @@ nsIOService::SpeculativeAnonymousConnect2(nsIURI *aURI,
return SpeculativeConnectInternal(aURI, aPrincipal, aCallbacks, true);
}
+/*static*/ bool
+nsIOService::BlockToplevelDataUriNavigations()
+{
+ return sBlockToplevelDataUriNavigations;
+}
+
} // namespace net
} // namespace mozilla
diff --git a/netwerk/base/nsIOService.h b/netwerk/base/nsIOService.h
index 7ac23b7910..e592c4d1c9 100644
--- a/netwerk/base/nsIOService.h
+++ b/netwerk/base/nsIOService.h
@@ -95,6 +95,8 @@ public:
bool IsLinkUp();
+ static bool BlockToplevelDataUriNavigations();
+
// Used to trigger a recheck of the captive portal status
nsresult RecheckCaptivePortal();
private:
@@ -176,6 +178,8 @@ private:
static bool sTelemetryEnabled;
+ static bool sBlockToplevelDataUriNavigations;
+
// These timestamps are needed for collecting telemetry on PR_Connect,
// PR_ConnectContinue and PR_Close blocking time. If we spend very long
// time in any of these functions we want to know if and what network
diff --git a/netwerk/base/nsIURI.idl b/netwerk/base/nsIURI.idl
index 2384c5fd99..ef163813aa 100644
--- a/netwerk/base/nsIURI.idl
+++ b/netwerk/base/nsIURI.idl
@@ -10,18 +10,18 @@
* provides accessors to set and query the most basic components of an URI.
* Subclasses, including nsIURL, impose greater structure on the URI.
*
- * This interface follows Tim Berners-Lee's URI spec (RFC2396) [1], where the
+ * This interface follows Tim Berners-Lee's URI spec (RFC3986) [1], where the
* basic URI components are defined as such:
* <pre>
- * ftp://username:password@hostname:portnumber/pathname#ref
- * \ / \ / \ / \ /\ \ /
- * - --------------- ------ -------- | -
- * | | | | | |
- * | | | | | Ref
- * | | | Port \ /
- * | | Host / --------
- * | UserPass / |
- * Scheme / Path
+ * ftp://username:password@hostname:portnumber/pathname?query#ref
+ * \ / \ / \ / \ /\ / \ / \ /
+ * - --------------- ------ -------- ------- --- -
+ * | | | | | | |
+ * | | | | FilePath Query Ref
+ * | | | Port \ /
+ * | | Host / ------------
+ * | UserPass / |
+ * Scheme / Path
* \ /
* --------------------------------
* |
@@ -30,13 +30,9 @@
* The definition of the URI components has been extended to allow for
* internationalized domain names [2] and the more generic IRI structure [3].
*
- * Note also that the RFC defines #-separated fragment identifiers as being
- * "not part of the URI". Despite this, we bundle them as part of the URI, for
- * convenience.
- *
- * [1] http://www.ietf.org/rfc/rfc2396.txt
- * [2] http://www.ietf.org/internet-drafts/draft-ietf-idn-idna-06.txt
- * [3] http://www.ietf.org/internet-drafts/draft-masinter-url-i18n-08.txt
+ * [1] https://tools.ietf.org/html/rfc3986
+ * [2] https://tools.ietf.org/html/rfc5890
+ * [3] https://tools.ietf.org/html/rfc3987
*/
%{C++
@@ -116,7 +112,7 @@ interface nsIURI : nsISupports
/**
* The Scheme is the protocol to which this URI refers. The scheme is
- * restricted to the US-ASCII charset per RFC2396. Setting this is
+ * restricted to the US-ASCII charset per RFC3986. Setting this is
* highly discouraged outside of a protocol handler implementation, since
* that will generally lead to incorrect results.
*/
@@ -174,6 +170,9 @@ interface nsIURI : nsISupports
* empty, depending on the protocol).
*
* Some characters may be escaped.
+ *
+ * This attribute contains query and ref parts for historical reasons.
+ * Use the 'filePath' attribute if you do not want those parts included.
*/
attribute AUTF8String path;
@@ -281,10 +280,31 @@ interface nsIURI : nsISupports
/**
* returns a string for the current URI with the ref element cleared.
*/
- readonly attribute AUTF8String specIgnoringRef;
+ readonly attribute AUTF8String specIgnoringRef;
/**
* Returns if there is a reference portion (the part after the "#") of the URI.
*/
- readonly attribute boolean hasRef;
+ readonly attribute boolean hasRef;
+
+ /************************************************************************
+ * Additional attributes added for .query support:
+ */
+
+ /**
+ * Returns a path including the directory and file portions of a
+ * URL. For example, the filePath of "http://host/foo/bar.html#baz"
+ * is "/foo/bar.html".
+ *
+ * Some characters may be escaped.
+ */
+ attribute AUTF8String filePath;
+
+ /**
+ * Returns the query portion (the part after the "?") of the URL.
+ * If there isn't one, an empty string is returned.
+ *
+ * Some characters may be escaped.
+ */
+ attribute AUTF8String query;
};
diff --git a/netwerk/base/nsIURIWithQuery.idl b/netwerk/base/nsIURIWithQuery.idl
deleted file mode 100644
index 749b2773d8..0000000000
--- a/netwerk/base/nsIURIWithQuery.idl
+++ /dev/null
@@ -1,30 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIURI.idl"
-
-/**
- * nsIURIWithQuery is implemented by URIs which have a query parameter.
- * This is useful for the URL API.
- */
-[scriptable, uuid(367510ee-8556-435a-8f99-b5fd357e08cc)]
-interface nsIURIWithQuery : nsIURI
-{
- /**
- * Returns a path including the directory and file portions of a
- * URL. For example, the filePath of "http://host/foo/bar.html#baz"
- * is "/foo/bar.html".
- *
- * Some characters may be escaped.
- */
- attribute AUTF8String filePath;
-
- /**
- * Returns the query portion (the part after the "?") of the URL.
- * If there isn't one, an empty string is returned.
- *
- * Some characters may be escaped.
- */
- attribute AUTF8String query;
-};
diff --git a/netwerk/base/nsIURL.idl b/netwerk/base/nsIURL.idl
index aeaa3f6949..9ff6c3dcda 100644
--- a/netwerk/base/nsIURL.idl
+++ b/netwerk/base/nsIURL.idl
@@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "nsIURIWithQuery.idl"
+#include "nsIURI.idl"
/**
* The nsIURL interface provides convenience methods that further
@@ -20,7 +20,7 @@
* filePath
*/
[scriptable, uuid(86adcd89-0b70-47a2-b0fe-5bb2c5f37e31)]
-interface nsIURL : nsIURIWithQuery
+interface nsIURL : nsIURI
{
/*************************************************************************
* The URL path is broken down into the following principal components:
@@ -28,7 +28,7 @@ interface nsIURL : nsIURIWithQuery
* attribute AUTF8String filePath;
* attribute AUTF8String query;
*
- * These are inherited from nsIURIWithQuery.
+ * These are inherited from nsIURI.
*/
/*************************************************************************
diff --git a/netwerk/base/nsSimpleURI.cpp b/netwerk/base/nsSimpleURI.cpp
index ae5c51a1e5..dbc0dc8179 100644
--- a/netwerk/base/nsSimpleURI.cpp
+++ b/netwerk/base/nsSimpleURI.cpp
@@ -48,7 +48,7 @@ nsSimpleURI::~nsSimpleURI()
NS_IMPL_ADDREF(nsSimpleURI)
NS_IMPL_RELEASE(nsSimpleURI)
NS_INTERFACE_TABLE_HEAD(nsSimpleURI)
-NS_INTERFACE_TABLE(nsSimpleURI, nsIURI, nsIURIWithQuery, nsISerializable,
+NS_INTERFACE_TABLE(nsSimpleURI, nsIURI, nsISerializable,
nsIClassInfo, nsIMutable, nsIIPCSerializableURI)
NS_INTERFACE_TABLE_TO_MAP_SEGUE
if (aIID.Equals(kThisSimpleURIImplementationCID))
@@ -784,10 +784,6 @@ nsSimpleURI::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
-//----------------------------------------------------------------------------
-// nsSimpleURI::nsIURIWithQuery
-//----------------------------------------------------------------------------
-
NS_IMETHODIMP
nsSimpleURI::GetFilePath(nsACString& aFilePath)
{
diff --git a/netwerk/base/nsSimpleURI.h b/netwerk/base/nsSimpleURI.h
index 29bc9b3139..842136ed63 100644
--- a/netwerk/base/nsSimpleURI.h
+++ b/netwerk/base/nsSimpleURI.h
@@ -8,7 +8,6 @@
#include "mozilla/MemoryReporting.h"
#include "nsIURI.h"
-#include "nsIURIWithQuery.h"
#include "nsISerializable.h"
#include "nsString.h"
#include "nsIClassInfo.h"
@@ -28,7 +27,7 @@ namespace net {
}
class nsSimpleURI
- : public nsIURIWithQuery
+ : public nsIURI
, public nsISerializable
, public nsIClassInfo
, public nsIMutable
@@ -41,7 +40,6 @@ protected:
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIURI
- NS_DECL_NSIURIWITHQUERY
NS_DECL_NSISERIALIZABLE
NS_DECL_NSICLASSINFO
NS_DECL_NSIMUTABLE
diff --git a/netwerk/base/nsStandardURL.cpp b/netwerk/base/nsStandardURL.cpp
index 0cdd2a7b5c..e2a290e4dc 100644
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -1182,7 +1182,6 @@ NS_IMPL_RELEASE(nsStandardURL)
NS_INTERFACE_MAP_BEGIN(nsStandardURL)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardURL)
NS_INTERFACE_MAP_ENTRY(nsIURI)
- NS_INTERFACE_MAP_ENTRY(nsIURIWithQuery)
NS_INTERFACE_MAP_ENTRY(nsIURL)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileURL, mSupportsFileURL)
NS_INTERFACE_MAP_ENTRY(nsIStandardURL)
diff --git a/netwerk/base/nsStandardURL.h b/netwerk/base/nsStandardURL.h
index 90f7f7db24..0ca345572a 100644
--- a/netwerk/base/nsStandardURL.h
+++ b/netwerk/base/nsStandardURL.h
@@ -54,7 +54,6 @@ protected:
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIURI
- NS_DECL_NSIURIWITHQUERY
NS_DECL_NSIURL
NS_DECL_NSIFILEURL
NS_DECL_NSISTANDARDURL
diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh
index 9365397d19..4f4dcf6a9b 100644
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -39,6 +39,7 @@ struct LoadInfoArgs
bool upgradeInsecureRequests;
bool verifySignedContent;
bool enforceSRI;
+ bool forceAllowDataURI;
bool forceInheritPrincipalDropped;
uint64_t innerWindowID;
uint64_t outerWindowID;
@@ -53,6 +54,7 @@ struct LoadInfoArgs
nsCString[] corsUnsafeHeaders;
bool forcePreflight;
bool isPreflight;
+ bool loadTriggeredFromExternal;
bool forceHSTSPriming;
bool mixedContentWouldBlock;
};
diff --git a/toolkit/modules/addons/WebRequestContent.js b/toolkit/modules/addons/WebRequestContent.js
index 219675e5b9..f044a1cd4e 100644
--- a/toolkit/modules/addons/WebRequestContent.js
+++ b/toolkit/modules/addons/WebRequestContent.js
@@ -80,6 +80,16 @@ var ContentPolicy = {
shouldLoad(policyType, contentLocation, requestOrigin,
node, mimeTypeGuess, extra, requestPrincipal) {
+
+ // Loads of TYPE_DOCUMENT and TYPE_SUBDOCUMENT perform a ConPol check
+ // within docshell as well as within the ContentSecurityManager. To avoid
+ // duplicate evaluations we ignore ConPol checks performed within docShell.
+ if (extra instanceof Ci.nsISupportsString) {
+ if (extra.data === "conPolCheckFromDocShell") {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+ }
+
if (requestPrincipal &&
Services.scriptSecurityManager.isSystemPrincipal(requestPrincipal)) {
return Ci.nsIContentPolicy.ACCEPT;