diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index 49fb4e7601b..febd4de1222 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -629,6 +629,42 @@ EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase) } } +static bool +AllSchemesMatch(nsIURI* aURI, nsIURI* aOtherURI) +{ + nsCOMPtr nestedURI = do_QueryInterface(aURI); + nsCOMPtr nestedOtherURI = do_QueryInterface(aOtherURI); + auto stringComparator = nsCaseInsensitiveCStringComparator(); + if (!nestedURI && !nestedOtherURI) { + // Neither of the URIs is nested, compare their schemes directly: + nsAutoCString scheme, otherScheme; + aURI->GetScheme(scheme); + aOtherURI->GetScheme(otherScheme); + return scheme.Equals(otherScheme, stringComparator); + } + while (nestedURI && nestedOtherURI) { + nsCOMPtr currentURI = do_QueryInterface(nestedURI); + nsCOMPtr currentOtherURI = do_QueryInterface(nestedOtherURI); + nsAutoCString scheme, otherScheme; + currentURI->GetScheme(scheme); + currentOtherURI->GetScheme(otherScheme); + if (!scheme.Equals(otherScheme, stringComparator)) { + return false; + } + + nestedURI->GetInnerURI(getter_AddRefs(currentURI)); + nestedOtherURI->GetInnerURI(getter_AddRefs(currentOtherURI)); + nestedURI = do_QueryInterface(currentURI); + nestedOtherURI = do_QueryInterface(currentOtherURI); + } + if (!!nestedURI != !!nestedOtherURI) { + // If only one of the scheme chains runs out at one point, clearly the chains + // aren't of the same length, so we bail: + return false; + } + return true; +} + NS_IMETHODIMP nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, nsIURI *aTargetURI, @@ -729,14 +765,30 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, rv = sourceBaseURI->GetScheme(sourceScheme); if (NS_FAILED(rv)) return rv; + // When comparing schemes, if the relevant pref is set, view-source URIs + // are reachable from same-protocol (so e.g. file: can link to + // view-source:file). This is required for reftests. + static bool sViewSourceReachableFromInner = false; + static bool sCachedViewSourcePref = false; + if (!sCachedViewSourcePref) { + sCachedViewSourcePref = true; + mozilla::Preferences::AddBoolVarCache(&sViewSourceReachableFromInner, + "security.view-source.reachable-from-inner-protocol"); + } + + bool targetIsViewSource = false; + if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) { // A null principal can target its own URI. if (sourceURI == aTargetURI) { return NS_OK; } } - else if (targetScheme.Equals(sourceScheme, - nsCaseInsensitiveCStringComparator())) + else if (AllSchemesMatch(sourceURI, aTargetURI) || + (sViewSourceReachableFromInner && + sourceScheme.EqualsIgnoreCase(targetScheme.get()) && + NS_SUCCEEDED(aTargetURI->SchemeIs("view-source", &targetIsViewSource)) && + targetIsViewSource)) { // every scheme can access another URI from the same scheme, // as long as they don't represent null principals... @@ -785,7 +837,7 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, // at the flags for our one URI. // Check for system target URI - rv = DenyAccessIfURIHasFlags(targetBaseURI, + rv = DenyAccessIfURIHasFlags(aTargetURI, nsIProtocolHandler::URI_DANGEROUS_TO_LOAD); if (NS_FAILED(rv)) { // Deny access, since the origin principal is not system @@ -853,7 +905,7 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, } // Check for target URI pointing to a file - rv = NS_URIChainHasFlags(targetBaseURI, + rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &hasFlags); NS_ENSURE_SUCCESS(rv, rv); @@ -1639,3 +1691,4 @@ nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv) return NS_OK; } + diff --git a/layout/tools/reftest/reftest-preferences.js b/layout/tools/reftest/reftest-preferences.js index ecf2ab111fe..bfc1569ae9f 100644 --- a/layout/tools/reftest/reftest-preferences.js +++ b/layout/tools/reftest/reftest-preferences.js @@ -64,3 +64,7 @@ // Allow XUL and XBL files to be opened from file:// URIs branch.setBoolPref("dom.allow_XUL_XBL_for_file", true); + + // Allow view-source URIs to be opened from URIs that share + // their protocol with the inner URI of the view-source URI + branch.setBoolPref("security.view-source.reachable-from-inner-protocol", true); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 8f8c822188e..274ac474d18 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2025,6 +2025,10 @@ pref("security.cert_pinning.enforcement_level", 0); // for tests. pref("security.cert_pinning.process_headers_from_non_builtin_roots", false); +// If set to true, allow view-source URIs to be opened from URIs that share +// their protocol with the inner URI of the view-source URI +pref("security.view-source.reachable-from-inner-protocol", false); + // Modifier key prefs: default to Windows settings, // menu access key = alt, accelerator key = control. // Use 17 for Ctrl, 18 for Alt, 224 for Meta, 91 for Win, 0 for none. Mac settings in macprefs.js diff --git a/netwerk/protocol/about/nsAboutProtocolHandler.cpp b/netwerk/protocol/about/nsAboutProtocolHandler.cpp index 70536345a12..91664952e0c 100644 --- a/netwerk/protocol/about/nsAboutProtocolHandler.cpp +++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp @@ -39,7 +39,8 @@ static bool IsSafeToLinkForUntrustedContent(nsIAboutModule *aModule, nsIURI *aUR } //////////////////////////////////////////////////////////////////////////////// -NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler, nsISupportsWeakReference) +NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler, + nsIProtocolHandlerWithDynamicFlags, nsISupportsWeakReference) //////////////////////////////////////////////////////////////////////////////// // nsIProtocolHandler methods: @@ -65,6 +66,33 @@ nsAboutProtocolHandler::GetProtocolFlags(uint32_t *result) return NS_OK; } +NS_IMETHODIMP +nsAboutProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags) +{ + // First use the default (which is "unsafe for content"): + GetProtocolFlags(aFlags); + + // Now try to see if this URI overrides the default: + nsCOMPtr aboutMod; + nsresult rv = NS_GetAboutModule(aURI, getter_AddRefs(aboutMod)); + if (NS_FAILED(rv)) { + // Swallow this and just tell the consumer the default: + return NS_OK; + } + uint32_t aboutModuleFlags = 0; + rv = aboutMod->GetURIFlags(aURI, &aboutModuleFlags); + // This should never happen, so pass back the error: + NS_ENSURE_SUCCESS(rv, rv); + + // If marked as safe, and not marked unlinkable, pass 'safe' flags. + if ((aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) && + !(aboutModuleFlags & nsIAboutModule::MAKE_UNLINKABLE)) { + *aFlags = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | + URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; + } + return NS_OK; +} + NS_IMETHODIMP nsAboutProtocolHandler::NewURI(const nsACString &aSpec, const char *aCharset, // ignore charset info diff --git a/netwerk/protocol/about/nsAboutProtocolHandler.h b/netwerk/protocol/about/nsAboutProtocolHandler.h index 007975ad46d..f3e9e19291a 100644 --- a/netwerk/protocol/about/nsAboutProtocolHandler.h +++ b/netwerk/protocol/about/nsAboutProtocolHandler.h @@ -13,7 +13,8 @@ class nsIURI; -class nsAboutProtocolHandler : public nsIProtocolHandler +class nsAboutProtocolHandler : public nsIProtocolHandlerWithDynamicFlags + , public nsIProtocolHandler , public nsSupportsWeakReference { public: @@ -21,6 +22,7 @@ public: // nsIProtocolHandler methods: NS_DECL_NSIPROTOCOLHANDLER + NS_DECL_NSIPROTOCOLHANDLERWITHDYNAMICFLAGS // nsAboutProtocolHandler methods: nsAboutProtocolHandler() {} diff --git a/netwerk/protocol/viewsource/nsViewSourceHandler.cpp b/netwerk/protocol/viewsource/nsViewSourceHandler.cpp index 5f8932b50e8..b346c2e900f 100644 --- a/netwerk/protocol/viewsource/nsViewSourceHandler.cpp +++ b/netwerk/protocol/viewsource/nsViewSourceHandler.cpp @@ -35,7 +35,7 @@ nsViewSourceHandler::GetDefaultPort(int32_t *result) NS_IMETHODIMP nsViewSourceHandler::GetProtocolFlags(uint32_t *result) { - *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | + *result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | URI_NON_PERSISTABLE; return NS_OK; } diff --git a/netwerk/test/mochitests/mochitest.ini b/netwerk/test/mochitests/mochitest.ini index da5a4f8acb7..d7e984618ee 100644 --- a/netwerk/test/mochitests/mochitest.ini +++ b/netwerk/test/mochitests/mochitest.ini @@ -25,6 +25,7 @@ skip-if = e10s [test_user_agent_updates.html] skip-if = e10s [test_user_agent_updates_reset.html] +[test_viewsource_unlinkable.html] [test_xhr_method_case.html] [test_signed_web_packaged_app.html] skip-if = e10s || buildapp != 'browser' diff --git a/netwerk/test/mochitests/test_viewsource_unlinkable.html b/netwerk/test/mochitests/test_viewsource_unlinkable.html new file mode 100644 index 00000000000..9a1a35d682d --- /dev/null +++ b/netwerk/test/mochitests/test_viewsource_unlinkable.html @@ -0,0 +1,27 @@ + + + + + Test for view-source linkability + + + + + + +

+ +
+
+ + + +