Backed out changeset b234ba179483 (bug 1747230) for causing mochitest failures on browser_target_blank.js. CLOSED TREE
This commit is contained in:
@@ -8533,9 +8533,8 @@ bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
|
|||||||
if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) {
|
if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) {
|
||||||
nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
|
nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
|
||||||
if (!docLoadInfo->GetLoadErrorPage() &&
|
if (!docLoadInfo->GetLoadErrorPage() &&
|
||||||
nsHTTPSOnlyUtils::ShouldUpgradeConnection(docLoadInfo) &&
|
nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(
|
||||||
nsHTTPSOnlyUtils::IsHttpDowngrade(currentExposableURI,
|
currentExposableURI, aLoadState->URI(), docLoadInfo)) {
|
||||||
aLoadState->URI())) {
|
|
||||||
uint32_t status = docLoadInfo->GetHttpsOnlyStatus();
|
uint32_t status = docLoadInfo->GetHttpsOnlyStatus();
|
||||||
if (status & (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
|
if (status & (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
|
||||||
nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) {
|
nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) {
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeWebSocket(nsIURI* aURI,
|
|||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
|
bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
|
||||||
nsIURI* aOldURI, nsIURI* aNewURI, nsILoadInfo* aLoadInfo,
|
nsIURI* aURI, nsILoadInfo* aLoadInfo,
|
||||||
const mozilla::EnumSet<UpgradeDowngradeEndlessLoopOptions>& aOptions) {
|
const mozilla::EnumSet<UpgradeDowngradeEndlessLoopOptions>& aOptions) {
|
||||||
// 1. Check if the HTTPS-Only/HTTPS-First is even enabled, before doing
|
// 1. Check if the HTTPS-Only/HTTPS-First is even enabled, before doing
|
||||||
// anything else
|
// anything else
|
||||||
@@ -307,47 +307,84 @@ bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Check HTTP 3xx redirects. If the Principal that kicked off the
|
// 5. If the URI to be loaded is not http, then it's defnitely no endless
|
||||||
|
// loop caused by https-only.
|
||||||
|
if (!aURI->SchemeIs("http")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString uriHost;
|
||||||
|
aURI->GetAsciiHost(uriHost);
|
||||||
|
|
||||||
|
auto uriAndPrincipalComparator = [&](nsIPrincipal* aPrincipal) {
|
||||||
|
nsAutoCString principalHost;
|
||||||
|
aPrincipal->GetAsciiHost(principalHost);
|
||||||
|
bool checkPath = mozilla::StaticPrefs::
|
||||||
|
dom_security_https_only_check_path_upgrade_downgrade_endless_loop();
|
||||||
|
if (!checkPath) {
|
||||||
|
return uriHost.Equals(principalHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString uriPath;
|
||||||
|
nsresult rv = aURI->GetFilePath(uriPath);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
nsAutoCString principalPath;
|
||||||
|
aPrincipal->GetFilePath(principalPath);
|
||||||
|
return uriHost.Equals(principalHost) && uriPath.Equals(principalPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
// We do dot return early when we find a loop, as we still need to set an
|
||||||
|
// exception at the end.
|
||||||
|
bool isLoop = false;
|
||||||
|
|
||||||
|
// 6. Check actual redirects. If the Principal that kicked off the
|
||||||
// load/redirect is not https, then it's definitely not a redirect cause by
|
// load/redirect is not https, then it's definitely not a redirect cause by
|
||||||
// https-only. If the scheme of the principal however is https and the
|
// https-only. If the scheme of the principal however is https and the
|
||||||
// asciiHost of the URI to be loaded and the asciiHost of the Principal are
|
// asciiHost of the URI to be loaded and the asciiHost of the Principal are
|
||||||
// identical, then we are dealing with an upgrade downgrade scenario and we
|
// identical, then we are dealing with an upgrade downgrade scenario and we
|
||||||
// have to break the cycle.
|
// have to break the cycle.
|
||||||
if (IsHttpDowngrade(aOldURI, aNewURI)) {
|
if (!aLoadInfo->RedirectChain().IsEmpty()) {
|
||||||
return true;
|
nsCOMPtr<nsIPrincipal> redirectPrincipal;
|
||||||
}
|
for (nsIRedirectHistoryEntry* entry : aLoadInfo->RedirectChain()) {
|
||||||
|
entry->GetPrincipal(getter_AddRefs(redirectPrincipal));
|
||||||
// TODO(Bug 1896691): Don't depend on triggeringPrincipal for JS/Meta
|
if (redirectPrincipal && redirectPrincipal->SchemeIs("https") &&
|
||||||
// redirects. Call this function at the correct places instead
|
uriAndPrincipalComparator(redirectPrincipal)) {
|
||||||
|
isLoop = true;
|
||||||
// 6. Bug 1725026: Disable JS/Meta loop detection when the load was triggered
|
}
|
||||||
// by a user gesture. This information is only when the redirect chain is
|
}
|
||||||
// empty. When the redirect chain is not empty, this load is definitely
|
} else {
|
||||||
// triggered by redirection, not a user gesture.
|
// 6.1 We should only check if this load is triggered by a user gesture
|
||||||
// TODO(1896685): Verify whether check is still necessary.
|
// when the redirect chain is empty, since this information is only useful
|
||||||
if (aLoadInfo->RedirectChain().IsEmpty()) {
|
// in our case here. When the redirect chain is not empty, this load is
|
||||||
|
// defnitely triggered by redirection, not a user gesture.
|
||||||
if (aLoadInfo->GetHasValidUserGestureActivation()) {
|
if (aLoadInfo->GetHasValidUserGestureActivation()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Meta redirects and JS based redirects (win.location). We detect them
|
if (!isLoop) {
|
||||||
// during the https upgrade internal redirect.
|
// 7. Meta redirects and JS based redirects (win.location). If the security
|
||||||
nsCOMPtr<nsIPrincipal> triggeringPrincipal = aLoadInfo->TriggeringPrincipal();
|
// context that triggered the load is not https, then it's defnitely no
|
||||||
if (!triggeringPrincipal->SchemeIs("https")) {
|
// endless loop caused by https-only. If the scheme is http however and the
|
||||||
return false;
|
// asciiHost of the URI to be loaded matches the asciiHost of the Principal,
|
||||||
|
// then we are dealing with an upgrade downgrade scenario and we have to
|
||||||
|
// break the cycle.
|
||||||
|
nsCOMPtr<nsIPrincipal> triggeringPrincipal =
|
||||||
|
aLoadInfo->TriggeringPrincipal();
|
||||||
|
if (!triggeringPrincipal->SchemeIs("https")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
isLoop = uriAndPrincipalComparator(triggeringPrincipal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We detect Meta and JS based redirects during the upgrade. Check whether
|
if (isLoop && enforceForHTTPSFirstMode &&
|
||||||
// we are currently in an upgrade situation here.
|
mozilla::StaticPrefs::
|
||||||
if (!IsHttpDowngrade(aNewURI, aOldURI)) {
|
dom_security_https_first_add_exception_on_failiure()) {
|
||||||
return false;
|
AddHTTPSFirstExceptionForSession(aURI, aLoadInfo);
|
||||||
}
|
}
|
||||||
// If we upgrade to the same URI that the load is origining from we are
|
|
||||||
// creating a redirect loop.
|
|
||||||
bool isLoop = false;
|
|
||||||
nsresult rv = triggeringPrincipal->EqualsURI(aNewURI, &isLoop);
|
|
||||||
NS_ENSURE_SUCCESS(rv, false);
|
|
||||||
return isLoop;
|
return isLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,8 +436,7 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// https-first needs to account for breaking upgrade-downgrade endless
|
// https-first needs to account for breaking upgrade-downgrade endless
|
||||||
// loops at this point for meta and js redirects because this function
|
// loops at this point because this function is called before we
|
||||||
// is called before we
|
|
||||||
// check the redirect limit in HttpBaseChannel. If we encounter
|
// check the redirect limit in HttpBaseChannel. If we encounter
|
||||||
// a same-origin server side downgrade from e.g https://example.com
|
// a same-origin server side downgrade from e.g https://example.com
|
||||||
// to http://example.com then we simply not annotating the loadinfo
|
// to http://example.com then we simply not annotating the loadinfo
|
||||||
@@ -408,18 +444,12 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
|
|||||||
// the handling for https-only mode is different from https-first mode,
|
// the handling for https-only mode is different from https-first mode,
|
||||||
// because https-only mode results in an exception page in case
|
// because https-only mode results in an exception page in case
|
||||||
// we encounter and endless upgrade downgrade loop.
|
// we encounter and endless upgrade downgrade loop.
|
||||||
/*
|
|
||||||
bool isUpgradeDowngradeEndlessLoop = IsUpgradeDowngradeEndlessLoop(
|
bool isUpgradeDowngradeEndlessLoop = IsUpgradeDowngradeEndlessLoop(
|
||||||
aURI, aURI, aLoadInfo,
|
aURI, aLoadInfo,
|
||||||
{UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSFirstMode});
|
{UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSFirstMode});
|
||||||
if (isUpgradeDowngradeEndlessLoop) {
|
if (isUpgradeDowngradeEndlessLoop) {
|
||||||
if (mozilla::StaticPrefs::
|
|
||||||
dom_security_https_first_add_exception_on_failiure()) {
|
|
||||||
nsHTTPSOnlyUtils::AddHTTPSFirstExceptionForSession(aURI, aLoadInfo);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// We can upgrade the request - let's log to the console and set the status
|
// We can upgrade the request - let's log to the console and set the status
|
||||||
// so we know that we upgraded the request.
|
// so we know that we upgraded the request.
|
||||||
@@ -870,51 +900,38 @@ bool nsHTTPSOnlyUtils::LoopbackOrLocalException(nsIURI* aURI) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool nsHTTPSOnlyUtils::ShouldUpgradeConnection(nsILoadInfo* aLoadInfo) {
|
bool nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(nsIURI* aHTTPSSchemeURI,
|
||||||
// Check if one of parameters is null then webpage can't be loaded yet
|
nsIURI* aOtherURI,
|
||||||
|
nsILoadInfo* aLoadInfo) {
|
||||||
|
// 1. Check if one of parameters is null then webpage can't be loaded yet
|
||||||
// and no further inspections are needed
|
// and no further inspections are needed
|
||||||
if (!aLoadInfo) {
|
if (!aHTTPSSchemeURI || !aOtherURI || !aLoadInfo) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the HTTPS-Only Mode is even enabled, before we do anything else
|
// 2. If the URI to be loaded is not http, then same origin will be detected
|
||||||
|
// already
|
||||||
|
if (!mozilla::net::SchemeIsHTTP(aOtherURI)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Check if the HTTPS-Only Mode is even enabled, before we do anything else
|
||||||
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
||||||
if (!IsHttpsOnlyModeEnabled(isPrivateWin) &&
|
if (!IsHttpsOnlyModeEnabled(isPrivateWin) &&
|
||||||
!IsHttpsFirstModeEnabled(isPrivateWin)) {
|
!IsHttpsFirstModeEnabled(isPrivateWin)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the load is exempt, then don't upgrade
|
// 4. If the load is exempt, then it's defintely not related to https-only
|
||||||
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
|
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
|
||||||
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
|
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */
|
// 5. Create a new target URI with 'https' instead of 'http' and compare it
|
||||||
bool nsHTTPSOnlyUtils::IsHttpDowngrade(nsIURI* aFromURI, nsIURI* aToURI) {
|
// to the current URI
|
||||||
MOZ_ASSERT(aFromURI);
|
|
||||||
MOZ_ASSERT(aToURI);
|
|
||||||
|
|
||||||
if (!aFromURI || !aToURI) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. If the target URI is not http, then it's not a http downgrade
|
|
||||||
if (!mozilla::net::SchemeIsHTTP(aToURI)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. If the origin URI isn't https, then it's not a http downgrade either.
|
|
||||||
if (!mozilla::net::SchemeIsHTTPS(aFromURI)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Create a new target URI with 'https' instead of 'http' and compare it
|
|
||||||
// to the origin URI
|
|
||||||
int32_t port = 0;
|
int32_t port = 0;
|
||||||
nsresult rv = aToURI->GetPort(&port);
|
nsresult rv = aOtherURI->GetPort(&port);
|
||||||
NS_ENSURE_SUCCESS(rv, false);
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
// a port of -1 indicates the default port, hence we upgrade from port 80 to
|
// a port of -1 indicates the default port, hence we upgrade from port 80 to
|
||||||
// port 443
|
// port 443
|
||||||
@@ -923,14 +940,15 @@ bool nsHTTPSOnlyUtils::IsHttpDowngrade(nsIURI* aFromURI, nsIURI* aToURI) {
|
|||||||
port = NS_GetDefaultPort("https");
|
port = NS_GetDefaultPort("https");
|
||||||
}
|
}
|
||||||
nsCOMPtr<nsIURI> newHTTPSchemeURI;
|
nsCOMPtr<nsIURI> newHTTPSchemeURI;
|
||||||
rv = NS_MutateURI(aToURI)
|
rv = NS_MutateURI(aOtherURI)
|
||||||
.SetScheme("https"_ns)
|
.SetScheme("https"_ns)
|
||||||
.SetPort(port)
|
.SetPort(port)
|
||||||
.Finalize(newHTTPSchemeURI);
|
.Finalize(newHTTPSchemeURI);
|
||||||
NS_ENSURE_SUCCESS(rv, false);
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
bool uriEquals = false;
|
bool uriEquals = false;
|
||||||
if (NS_FAILED(aFromURI->EqualsExceptRef(newHTTPSchemeURI, &uriEquals))) {
|
if (NS_FAILED(
|
||||||
|
aHTTPSSchemeURI->EqualsExceptRef(newHTTPSchemeURI, &uriEquals))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class nsHTTPSOnlyUtils {
|
|||||||
EnforceForHTTPSRR,
|
EnforceForHTTPSRR,
|
||||||
};
|
};
|
||||||
static bool IsUpgradeDowngradeEndlessLoop(
|
static bool IsUpgradeDowngradeEndlessLoop(
|
||||||
nsIURI* aOldURI, nsIURI* aNewURI, nsILoadInfo* aLoadInfo,
|
nsIURI* aURI, nsILoadInfo* aLoadInfo,
|
||||||
const mozilla::EnumSet<UpgradeDowngradeEndlessLoopOptions>& aOptions =
|
const mozilla::EnumSet<UpgradeDowngradeEndlessLoopOptions>& aOptions =
|
||||||
{});
|
{});
|
||||||
|
|
||||||
@@ -153,20 +153,17 @@ class nsHTTPSOnlyUtils {
|
|||||||
*/
|
*/
|
||||||
static bool IsSafeToAcceptCORSOrMixedContent(nsILoadInfo* aLoadInfo);
|
static bool IsSafeToAcceptCORSOrMixedContent(nsILoadInfo* aLoadInfo);
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if https only or https first mode is enabled for this load
|
|
||||||
* @param aLoadInfo nsILoadInfo of the request
|
|
||||||
*/
|
|
||||||
static bool ShouldUpgradeConnection(nsILoadInfo* aLoadInfo);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if two URIs are same origin modulo the difference that
|
* Checks if two URIs are same origin modulo the difference that
|
||||||
* aToURI scheme is downgraded to http from https aFromURI.
|
* aHTTPSchemeURI uses an http scheme.
|
||||||
* @param aFromURI nsIURI using scheme of https
|
* @param aHTTPSSchemeURI nsIURI using scheme of https
|
||||||
* @param aToURI nsIURI using scheme of http
|
* @param aOtherURI nsIURI using scheme of http
|
||||||
|
* @param aLoadInfo nsILoadInfo of the request
|
||||||
* @return true, if URIs are equal except scheme and ref
|
* @return true, if URIs are equal except scheme and ref
|
||||||
*/
|
*/
|
||||||
static bool IsHttpDowngrade(nsIURI* aFromURI, nsIURI* aToURI);
|
static bool IsEqualURIExceptSchemeAndRef(nsIURI* aHTTPSSchemeURI,
|
||||||
|
nsIURI* aOtherURI,
|
||||||
|
nsILoadInfo* aLoadInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will add a special temporary HTTPS-Only exception that only applies to
|
* Will add a special temporary HTTPS-Only exception that only applies to
|
||||||
|
|||||||
@@ -1,96 +1,61 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// DOWNGRADE_REDIRECT_*: http instead of https, otherwise same path
|
const REDIRECT_URI =
|
||||||
const DOWNGRADE_REDIRECT_META = `
|
"http://example.com/tests/dom/security/test/https-first/file_break_endless_upgrade_downgrade_loop.sjs?verify";
|
||||||
<html>
|
const DOWNGRADE_URI =
|
||||||
<head>
|
"http://example.com/tests/dom/security/test/https-first/file_downgrade_with_different_path.sjs";
|
||||||
<meta http-equiv="refresh" content="0; url='http://example.com/tests/dom/security/test/https-first/file_break_endless_upgrade_downgrade_loop.sjs?downgrade_redirect_meta'">
|
const RESPONSE_ERROR = "unexpected-query";
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
META REDIRECT
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
|
|
||||||
const DOWNGRADE_REDIRECT_JS = `
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
JS REDIRECT
|
|
||||||
<script>
|
|
||||||
let url= "http://example.com/tests/dom/security/test/https-first/file_break_endless_upgrade_downgrade_loop.sjs?downgrade_redirect_js";
|
|
||||||
window.location = url;
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
|
|
||||||
// REDIRECT_*: different path and http instead of https
|
|
||||||
const REDIRECT_META = `
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0; url='http://example.com/tests/dom/security/test/https-first/file_downgrade_with_different_path.sjs?redirect_meta'">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
META REDIRECT
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
|
|
||||||
const REDIRECT_JS = `
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
JS REDIRECT
|
|
||||||
<script>
|
|
||||||
let url= "http://example.com/tests/dom/security/test/https-first/file_downgrade_with_different_path.sjs?redirect_js";
|
|
||||||
window.location = url;
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
|
|
||||||
// An onload postmessage to window opener
|
// An onload postmessage to window opener
|
||||||
|
const RESPONSE_HTTPS_SCHEME = `
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<script type="application/javascript">
|
||||||
|
window.opener.postMessage({result: 'scheme-https'}, '*');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
|
||||||
const RESPONSE_HTTP_SCHEME = `
|
const RESPONSE_HTTP_SCHEME = `
|
||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
window.opener.postMessage({result: 'scheme-http-'+window.location}, '*');
|
window.opener.postMessage({result: 'scheme-http'}, '*');
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
|
|
||||||
function handleRequest(request, response) {
|
function handleRequest(request, response) {
|
||||||
response.setHeader("Cache-Control", "no-cache", false);
|
response.setHeader("Cache-Control", "no-cache", false);
|
||||||
|
const query = request.queryString;
|
||||||
|
|
||||||
if (request.scheme == "https") {
|
if (query == "downgrade") {
|
||||||
// allow http status code as parameter
|
// send same-origin downgrade from https: to http: with a different path.
|
||||||
const query = request.queryString.split("=");
|
// we don't consider it's an endless upgrade downgrade loop in this case.
|
||||||
if (query[0] == "downgrade_redirect_http") {
|
response.setStatusLine(request.httpVersion, 302, "Found");
|
||||||
let location = `http://${request.host}${request.path}?${request.queryString}`;
|
response.setHeader("Location", DOWNGRADE_URI, false);
|
||||||
response.setStatusLine(request.httpVersion, query[1], "Found");
|
|
||||||
response.setHeader("Location", location, false);
|
|
||||||
} else if (query[0] == "redirect_http") {
|
|
||||||
response.setStatusLine(request.httpVersion, query[1], "Found");
|
|
||||||
let location =
|
|
||||||
"http://example.com/tests/dom/security/test/https-first/file_downgrade_with_different_path.sjs?" +
|
|
||||||
request.queryString;
|
|
||||||
response.setHeader("Location", location, false);
|
|
||||||
} else if (query[0] == "downgrade_redirect_js") {
|
|
||||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
||||||
response.write(DOWNGRADE_REDIRECT_JS);
|
|
||||||
} else if (query[0] == "redirect_js") {
|
|
||||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
||||||
response.write(REDIRECT_JS);
|
|
||||||
} else if (query[0] == "downgrade_redirect_meta") {
|
|
||||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
||||||
response.write(DOWNGRADE_REDIRECT_META);
|
|
||||||
} else if (query[0] == "redirect_meta") {
|
|
||||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
||||||
response.write(REDIRECT_META);
|
|
||||||
} else {
|
|
||||||
// We should never get here, but just in case ...
|
|
||||||
response.setStatusLine(request.httpVersion, 500, "OK");
|
|
||||||
response.write("unexepcted query");
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return http response
|
// handle the redirect case
|
||||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
if ((query >= 301 && query <= 303) || query == 307) {
|
||||||
response.write(RESPONSE_HTTP_SCHEME);
|
// send same-origin downgrade from https: to http: again simluating
|
||||||
|
// and endless upgrade downgrade loop.
|
||||||
|
response.setStatusLine(request.httpVersion, query, "Found");
|
||||||
|
response.setHeader("Location", REDIRECT_URI, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if scheme is http:// or https://
|
||||||
|
if (query == "verify") {
|
||||||
|
let response_content =
|
||||||
|
request.scheme === "https" ? RESPONSE_HTTPS_SCHEME : RESPONSE_HTTP_SCHEME;
|
||||||
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||||
|
response.write(response_content);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should never get here, but just in case ...
|
||||||
|
response.setStatusLine(request.httpVersion, 500, "OK");
|
||||||
|
response.write("unexepcted query");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const RESPONSE_HTTPS_SCHEME = `
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
window.opener.postMessage({result: 'scheme-https-'+window.location}, '*');
|
window.opener.postMessage({result: 'scheme-https'}, '*');
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
@@ -14,7 +14,7 @@ const RESPONSE_HTTP_SCHEME = `
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
window.opener.postMessage({result: 'scheme-http-'+window.location}'}, '*');
|
window.opener.postMessage({result: 'scheme-http'}, '*');
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ const OTHERHOST_REDIRECT_URI_HTTP =
|
|||||||
"http://example.org/tests/dom/security/test/https-first/file_multiple_redirection.sjs?verify";
|
"http://example.org/tests/dom/security/test/https-first/file_multiple_redirection.sjs?verify";
|
||||||
const REDIRECT_URI_HTTPS =
|
const REDIRECT_URI_HTTPS =
|
||||||
"https://example.com/tests/dom/security/test/https-first/file_multiple_redirection.sjs?verify";
|
"https://example.com/tests/dom/security/test/https-first/file_multiple_redirection.sjs?verify";
|
||||||
const REDIRECT_DOWNGRADE_URI =
|
|
||||||
"https://example.com/tests/dom/security/test/https-first/file_multiple_redirection.sjs?downgrade";
|
|
||||||
const REDIRECT_DOWNGRADE_URI_HTTP =
|
|
||||||
"http://example.com/tests/dom/security/test/https-first/file_multiple_redirection.sjs?downgrade";
|
|
||||||
|
|
||||||
const RESPONSE_ERROR = "unexpected-query";
|
const RESPONSE_ERROR = "unexpected-query";
|
||||||
|
|
||||||
@@ -56,10 +52,6 @@ function sendRedirection(query, response) {
|
|||||||
if (query.includes("test4")) {
|
if (query.includes("test4")) {
|
||||||
response.setHeader("Location", OTHERHOST_REDIRECT_URI_HTTP, false);
|
response.setHeader("Location", OTHERHOST_REDIRECT_URI_HTTP, false);
|
||||||
}
|
}
|
||||||
// send a redirection http downgrade uri
|
|
||||||
if (query.includes("test5")) {
|
|
||||||
response.setHeader("Location", REDIRECT_DOWNGRADE_URI, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRequest(request, response) {
|
function handleRequest(request, response) {
|
||||||
@@ -72,7 +64,6 @@ function handleRequest(request, response) {
|
|||||||
if (request.scheme !== "https") {
|
if (request.scheme !== "https") {
|
||||||
response.setStatusLine(request.httpVersion, 500, "OK");
|
response.setStatusLine(request.httpVersion, 500, "OK");
|
||||||
response.write("Request should have been HTTPS.");
|
response.write("Request should have been HTTPS.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// send a 302 redirection
|
// send a 302 redirection
|
||||||
response.setStatusLine(request.httpVersion, 302, "Found");
|
response.setStatusLine(request.httpVersion, 302, "Found");
|
||||||
@@ -84,20 +75,11 @@ function handleRequest(request, response) {
|
|||||||
if (request.scheme !== "https") {
|
if (request.scheme !== "https") {
|
||||||
response.setStatusLine(request.httpVersion, 500, "OK");
|
response.setStatusLine(request.httpVersion, 500, "OK");
|
||||||
response.write("Request should have been HTTPS.");
|
response.write("Request should have been HTTPS.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
response.setStatusLine(request.httpVersion, 302, "Found");
|
response.setStatusLine(request.httpVersion, 302, "Found");
|
||||||
sendRedirection(query, response);
|
sendRedirection(query, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Send a http redirect downgrade
|
|
||||||
if (query.includes("downgrade")) {
|
|
||||||
response.setStatusLine(request.httpVersion, 302, "Found");
|
|
||||||
let redirect_uri =
|
|
||||||
request.scheme === "https" ? REDIRECT_DOWNGRADE_URI : REDIRECT_URI_HTTP;
|
|
||||||
response.setHeader("Location", redirect_uri, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Reset the HSTS policy, prevent influencing other tests
|
// Reset the HSTS policy, prevent influencing other tests
|
||||||
if (request.queryString === "reset") {
|
if (request.queryString === "reset") {
|
||||||
response.setHeader("Strict-Transport-Security", "max-age=0");
|
response.setHeader("Strict-Transport-Security", "max-age=0");
|
||||||
|
|||||||
@@ -20,64 +20,55 @@ Test that same origin redirect does not cause endless loop with https-first enab
|
|||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
let testQueries = [
|
const redirectCodes = ["301", "302","303","307"];
|
||||||
// Those are clear downgrades. Need to load http site
|
|
||||||
{ query: "downgrade_redirect_meta", result: "http" },
|
|
||||||
{ query: "downgrade_redirect_js", result: "http" },
|
|
||||||
{ query: "downgrade_redirect_http=301", result: "http" },
|
|
||||||
{ query: "downgrade_redirect_http=302", result: "http" },
|
|
||||||
{ query: "downgrade_redirect_http=303", result: "http" },
|
|
||||||
{ query: "downgrade_redirect_http=307", result: "http" },
|
|
||||||
// from here it isn't required to downgrade. Could be upgraded again
|
|
||||||
{ query: "redirect_meta", result: "https" },
|
|
||||||
{ query: "redirect_js", result: "https" },
|
|
||||||
{ query: "redirect_http=301", result: "https" },
|
|
||||||
{ query: "redirect_http=302", result: "https" },
|
|
||||||
{ query: "redirect_http=303", result: "https" },
|
|
||||||
{ query: "redirect_http=307", result: "https" },
|
|
||||||
];
|
|
||||||
let currentTest = 0;
|
let currentTest = 0;
|
||||||
// do each test two time. One time starting with https:// one time with http://
|
|
||||||
let currentTestStartWithHttps = false;
|
|
||||||
let testWin;
|
let testWin;
|
||||||
window.addEventListener("message", receiveMessage);
|
window.addEventListener("message", receiveMessage);
|
||||||
|
|
||||||
// receive message from loaded site verifying the scheme of
|
// receive message from loaded site verifying the scheme of
|
||||||
// the loaded document.
|
// the loaded document.
|
||||||
async function receiveMessage(event) {
|
async function receiveMessage(event) {
|
||||||
let currentTestParams = testQueries[Math.floor(currentTest / 2)];
|
let currentRedirectCode = redirectCodes[currentTest];
|
||||||
let expectedURI;
|
is(event.data.result,
|
||||||
if(currentTestParams.result == "https") {
|
"scheme-http",
|
||||||
expectedURI = "https://example.com/tests/dom/security/test/https-first/file_downgrade_with_different_path.sjs?" + currentTestParams.query;
|
"same-origin redirect results in 'http' for " + currentRedirectCode
|
||||||
} else {
|
|
||||||
expectedURI = "http://example.com/tests/dom/security/test/https-first/file_break_endless_upgrade_downgrade_loop.sjs?" + currentTestParams.query;
|
|
||||||
}
|
|
||||||
is(`scheme-${currentTestParams.result}-${expectedURI}`,
|
|
||||||
event.data.result,
|
|
||||||
`${currentTest}: redirect results in '${currentTestParams.result}' for ${expectedURI}`
|
|
||||||
);
|
);
|
||||||
testWin.close();
|
testWin.close();
|
||||||
await SpecialPowers.removePermission(
|
await SpecialPowers.removePermission(
|
||||||
"https-only-load-insecure",
|
"https-only-load-insecure",
|
||||||
"http://example.com"
|
"http://example.com"
|
||||||
);
|
);
|
||||||
// each test gets run starting with http:// and https://. Therefore *2
|
if (++currentTest < redirectCodes.length) {
|
||||||
if (++currentTest < 2 * testQueries.length) {
|
|
||||||
// start next case
|
|
||||||
startTest();
|
startTest();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// cleanup
|
|
||||||
window.removeEventListener("message", receiveMessage);
|
window.removeEventListener("message", receiveMessage);
|
||||||
|
window.addEventListener("message", receiveMessageForDifferentPathTest);
|
||||||
|
testDifferentPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function receiveMessageForDifferentPathTest(event) {
|
||||||
|
is(event.data.result,
|
||||||
|
"scheme-https",
|
||||||
|
"scheme should be https when the path is different"
|
||||||
|
);
|
||||||
|
testWin.close();
|
||||||
|
window.removeEventListener("message", receiveMessageForDifferentPathTest);
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startTest() {
|
async function startTest() {
|
||||||
const currentTestParams = testQueries[Math.floor(currentTest / 2)];
|
const currentCode = redirectCodes[currentTest];
|
||||||
const scheme = currentTest % 2 == 0 ? "https" : "http";
|
|
||||||
// Load an http:// window which gets upgraded to https://
|
// Load an http:// window which gets upgraded to https://
|
||||||
let uri =
|
let uri =
|
||||||
`${scheme}://example.com/tests/dom/security/test/https-first/file_break_endless_upgrade_downgrade_loop.sjs?${currentTestParams.query}`;
|
`http://example.com/tests/dom/security/test/https-first/file_break_endless_upgrade_downgrade_loop.sjs?${currentCode}`;
|
||||||
|
testWin = window.open(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testDifferentPath() {
|
||||||
|
// Load an https:// window which gets downgraded to http://
|
||||||
|
let uri =
|
||||||
|
`https://example.com/tests/dom/security/test/https-first/file_break_endless_upgrade_downgrade_loop.sjs?downgrade`;
|
||||||
testWin = window.open(uri);
|
testWin = window.open(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +77,7 @@ Test that same origin redirect does not cause endless loop with https-first enab
|
|||||||
["dom.security.https_first", true],
|
["dom.security.https_first", true],
|
||||||
["security.mixed_content.block_active_content", false],
|
["security.mixed_content.block_active_content", false],
|
||||||
["security.mixed_content.block_display_content", false],
|
["security.mixed_content.block_display_content", false],
|
||||||
|
["dom.security.https_only_check_path_upgrade_downgrade_endless_loop", true],
|
||||||
]}, startTest);
|
]}, startTest);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ Test multiple redirects using https-first and ensure the entire redirect chain i
|
|||||||
const testCase = [
|
const testCase = [
|
||||||
// test 1: https-first upgrades http://example.com/test1 -> https://example.com/test1
|
// test 1: https-first upgrades http://example.com/test1 -> https://example.com/test1
|
||||||
// that's redirect to https://example.com/.../redirect which then redirects
|
// that's redirect to https://example.com/.../redirect which then redirects
|
||||||
// to http://example.com/../verify.
|
// to http://example.com/../verify. Since the last redirect is http, and the
|
||||||
{name: "test last redirect HTTP", result: "scheme-https", query: "test1" },
|
// the redirection chain contains already example.com we expect https-first
|
||||||
|
// to downgrade the request.
|
||||||
|
{name: "test last redirect HTTP", result: "scheme-http", query: "test1" },
|
||||||
// test 2: https-first upgrades http://example.com/test2 -> https://example.com/test2
|
// test 2: https-first upgrades http://example.com/test2 -> https://example.com/test2
|
||||||
// that's redirect to https://example.com/.../redirect which then redirects
|
// that's redirect to https://example.com/.../redirect which then redirects
|
||||||
// to https://example.com/../verify. Since the last redirect is https, we
|
// to https://example.com/../verify. Since the last redirect is https, we
|
||||||
@@ -41,13 +43,6 @@ Test multiple redirects using https-first and ensure the entire redirect chain i
|
|||||||
// http://example.org/.../verify -upgrade-> httpS://example.ORG/.../verify
|
// http://example.org/.../verify -upgrade-> httpS://example.ORG/.../verify
|
||||||
// Everything should be upgraded and accessed only via HTTPS!
|
// Everything should be upgraded and accessed only via HTTPS!
|
||||||
{name: "test last redirect other HTTP origin gets upgraded", result: "scheme-https", query: "test4" },
|
{name: "test last redirect other HTTP origin gets upgraded", result: "scheme-https", query: "test4" },
|
||||||
// test 5: https-first upgrades http://example.com/test5 -> https://example.com/test5
|
|
||||||
// that's redirect to https://example.com/.../downgrade which then redirects
|
|
||||||
// https-first upgrades http://example.com/.../downgrade -> https://example.com/.../downgrade
|
|
||||||
// that's redirect to http://example.com/.../downgrade which which is detected as http downgrade
|
|
||||||
// to http://example.com/../verify. Since the last redirect is http, and we
|
|
||||||
// had a downgrade in the redirect chain. We load the http version
|
|
||||||
{name: "test downgrade HTTP", result: "scheme-http", query: "test5" },
|
|
||||||
]
|
]
|
||||||
let currentTest = 0;
|
let currentTest = 0;
|
||||||
let testWin;
|
let testWin;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
const REDIRECT_META = `
|
const REDIRECT_META = `
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="refresh" content="0; url='http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?test1'">
|
<meta http-equiv="refresh" content="0; url='http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?test1b'">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
META REDIRECT
|
META REDIRECT
|
||||||
@@ -16,17 +16,17 @@ const REDIRECT_JS = `
|
|||||||
<body>
|
<body>
|
||||||
JS REDIRECT
|
JS REDIRECT
|
||||||
<script>
|
<script>
|
||||||
let url= "http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?test2";
|
let url= "http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?test2b";
|
||||||
window.location = url;
|
window.location = url;
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
|
|
||||||
const REDIRECT_302 =
|
const REDIRECT_302 =
|
||||||
"http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?test3";
|
"http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?test3b";
|
||||||
|
|
||||||
const REDIRECT_302_DIFFERENT_PATH =
|
const REDIRECT_302_DIFFERENT_PATH =
|
||||||
"http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?verify";
|
"http://example.com/tests/dom/security/test/https-only/file_user_gesture.html";
|
||||||
|
|
||||||
function handleRequest(request, response) {
|
function handleRequest(request, response) {
|
||||||
// avoid confusing cache behaviour
|
// avoid confusing cache behaviour
|
||||||
@@ -35,35 +35,30 @@ function handleRequest(request, response) {
|
|||||||
|
|
||||||
// if the scheme is not https, meaning that the initial request did not
|
// if the scheme is not https, meaning that the initial request did not
|
||||||
// get upgraded, then we rather fall through and display unexpected content.
|
// get upgraded, then we rather fall through and display unexpected content.
|
||||||
if (request.scheme == "https") {
|
if (request.scheme === "https") {
|
||||||
let query = request.queryString;
|
let query = request.queryString;
|
||||||
|
|
||||||
if (query == "test1") {
|
if (query === "test1a") {
|
||||||
response.write(REDIRECT_META);
|
response.write(REDIRECT_META);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query == "test2") {
|
if (query === "test2a") {
|
||||||
response.write(REDIRECT_JS);
|
response.write(REDIRECT_JS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query == "test3") {
|
if (query === "test3a") {
|
||||||
response.setStatusLine("1.1", 302, "Found");
|
response.setStatusLine("1.1", 302, "Found");
|
||||||
response.setHeader("Location", REDIRECT_302, false);
|
response.setHeader("Location", REDIRECT_302, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query == "test4") {
|
if (query === "test4a") {
|
||||||
response.setStatusLine("1.1", 302, "Found");
|
response.setStatusLine("1.1", 302, "Found");
|
||||||
response.setHeader("Location", REDIRECT_302_DIFFERENT_PATH, false);
|
response.setHeader("Location", REDIRECT_302_DIFFERENT_PATH, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query == "verify") {
|
|
||||||
response.write("<html><body>OK :)</body></html>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we should never get here, just in case,
|
// we should never get here, just in case,
|
||||||
|
|||||||
@@ -16,85 +16,73 @@
|
|||||||
* Test 1: Meta Refresh
|
* Test 1: Meta Refresh
|
||||||
* Test 2: JS Redirect
|
* Test 2: JS Redirect
|
||||||
* Test 3: 302 redirect
|
* Test 3: 302 redirect
|
||||||
* Test 4: Redirect to different origin. No redirect loop should be detected
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
SimpleTest.requestFlakyTimeout("We need to wait for the HTTPS-Only error page to appear");
|
||||||
|
SimpleTest.requestLongerTimeout(10);
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
const HTTP_REQUEST_URL =
|
const REQUEST_URL =
|
||||||
"http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs";
|
"http://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs";
|
||||||
const HTTPS_REQUEST_URL =
|
|
||||||
"https://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs";
|
|
||||||
|
|
||||||
const testQueries = [
|
function resolveAfter6Seconds() {
|
||||||
// Test 1: Meta Refresh Redirect
|
return new Promise(resolve => {
|
||||||
{ scheme: "http", query: "test1", error: true },
|
setTimeout(() => {
|
||||||
{ scheme: "https", query: "test1", error: true },
|
resolve();
|
||||||
// Test 2: JS win.location Redirect
|
}, 6000);
|
||||||
{ scheme: "http", query: "test2", error: true },
|
});
|
||||||
{ scheme: "https", query: "test2", error: true },
|
}
|
||||||
// Test 3: 302 Redirect
|
|
||||||
{ scheme: "http", query: "test3", error: true },
|
|
||||||
{ scheme: "https", query: "test3", error: true },
|
|
||||||
// Test 4: 302 Redirect with a different path
|
|
||||||
{ scheme: "http", query: "test4", error: false },
|
|
||||||
{ scheme: "https", query: "test4", error: false },
|
|
||||||
];
|
|
||||||
|
|
||||||
let currentTest = 0;
|
async function verifyResult(aTestName) {
|
||||||
// do each test two time. One time starting with https:// one time with http://
|
let errorPageL10nId = "about-httpsonly-title-alert";
|
||||||
let testWin;
|
let innerHTML = content.document.body.innerHTML;
|
||||||
window.addEventListener("message", receiveMessageWhenLoaded);
|
ok(innerHTML.includes(errorPageL10nId), "the error page should be shown for " + aTestName);
|
||||||
|
}
|
||||||
|
|
||||||
function postMessageWhenLoaded() {
|
async function verifyTest4Result() {
|
||||||
SimpleTest.waitForCondition(async () => {
|
let pathname = content.document.location.pathname;
|
||||||
return await SpecialPowers.spawn(testWin, [], () => {
|
ok(
|
||||||
let innerHTML = content.document.body.innerHTML;
|
pathname === "/tests/dom/security/test/https-only/file_user_gesture.html",
|
||||||
return innerHTML == "OK :)"
|
"the http:// page should be loaded"
|
||||||
|| innerHTML == "DO NOT DISPLAY THIS"
|
|
||||||
|| innerHTML.includes("about-httpsonly-title-alert");
|
|
||||||
}).catch(() => false);
|
|
||||||
},
|
|
||||||
() => window.postMessage("https-only-page-loaded", "*"),
|
|
||||||
"waiting for page load to complete"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function receiveMessageWhenLoaded() {
|
async function runTests() {
|
||||||
const currentTestParams = testQueries[currentTest];
|
await SpecialPowers.pushPrefEnv({ set: [
|
||||||
let testName = currentTestParams.scheme + ":" + currentTestParams.query
|
["dom.security.https_only_mode", true],
|
||||||
|
["dom.security.https_only_mode_break_upgrade_downgrade_endless_loop", true],
|
||||||
|
["dom.security.https_only_check_path_upgrade_downgrade_endless_loop", true],
|
||||||
|
]});
|
||||||
|
|
||||||
let innerHTML = await SpecialPowers.spawn(testWin, [], () => {
|
// Test 1: Meta Refresh Redirect
|
||||||
return content.document.body.innerHTML;
|
let winTest1 = window.open(REQUEST_URL + "?test1a", "_blank");
|
||||||
});
|
// Test 2: JS win.location Redirect
|
||||||
if(currentTestParams.error) {
|
let winTest2 = window.open(REQUEST_URL + "?test2a", "_blank");
|
||||||
ok(innerHTML.includes("about-httpsonly-title-alert"), testName + ": the error page should be shown");
|
// Test 3: 302 Redirect
|
||||||
} else {
|
let winTest3 = window.open(REQUEST_URL + "?test3a", "_blank");
|
||||||
is(innerHTML, "OK :)", testName + ": different path with https loaded ");
|
// Test 4: 302 Redirect with a different path
|
||||||
}
|
let winTest4 = window.open(REQUEST_URL + "?test4a", "_blank");
|
||||||
testWin.close();
|
|
||||||
|
// provide enough time for:
|
||||||
|
// the redirects to occur, and the error page to be displayed
|
||||||
|
await resolveAfter6Seconds();
|
||||||
|
|
||||||
|
await SpecialPowers.spawn(winTest1, ["test1"], verifyResult);
|
||||||
|
winTest1.close();
|
||||||
|
|
||||||
|
await SpecialPowers.spawn(winTest2, ["test2"], verifyResult);
|
||||||
|
winTest2.close();
|
||||||
|
|
||||||
|
await SpecialPowers.spawn(winTest3, ["test3"], verifyResult);
|
||||||
|
winTest3.close();
|
||||||
|
|
||||||
|
await SpecialPowers.spawn(winTest4, ["test4"], verifyTest4Result);
|
||||||
|
winTest4.close();
|
||||||
|
|
||||||
if (++currentTest < testQueries.length) {
|
|
||||||
runNextTest();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// no more tests to run -> cleanup
|
|
||||||
window.removeEventListener("https-only-page-load", receiveMessageWhenLoaded);
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runNextTest() {
|
runTests();
|
||||||
const currentTestParams = testQueries[currentTest];
|
|
||||||
let uri = `${currentTestParams.scheme}://example.com/tests/dom/security/test/https-only/file_break_endless_upgrade_downgrade_loop.sjs?${currentTestParams.query}`;
|
|
||||||
testWin = window.open(uri, "_blank");
|
|
||||||
postMessageWhenLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
SpecialPowers.pushPrefEnv({ set: [
|
|
||||||
["dom.security.https_only_mode", true],
|
|
||||||
["dom.security.https_only_mode_break_upgrade_downgrade_endless_loop", true],
|
|
||||||
["dom.security.https_only_mode_ever_enabled", true], // clear this pref at the end
|
|
||||||
]}, runNextTest);
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -3735,6 +3735,13 @@
|
|||||||
value: true
|
value: true
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
# If true, when checking if it's upgrade downgrade cycles, the URI path will be
|
||||||
|
# also checked.
|
||||||
|
- name: dom.security.https_only_check_path_upgrade_downgrade_endless_loop
|
||||||
|
type: RelaxedAtomicBool
|
||||||
|
value: true
|
||||||
|
mirror: always
|
||||||
|
|
||||||
# If true and HTTPS-only mode is enabled, requests
|
# If true and HTTPS-only mode is enabled, requests
|
||||||
# to local IP addresses are also upgraded
|
# to local IP addresses are also upgraded
|
||||||
- name: dom.security.https_only_mode.upgrade_local
|
- name: dom.security.https_only_mode.upgrade_local
|
||||||
|
|||||||
@@ -5969,8 +5969,7 @@ HttpBaseChannel::SetNavigationStartTimeStamp(TimeStamp aTimeStamp) {
|
|||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult HttpBaseChannel::CheckRedirectLimit(nsIURI* aNewURI,
|
nsresult HttpBaseChannel::CheckRedirectLimit(uint32_t aRedirectFlags) const {
|
||||||
uint32_t aRedirectFlags) const {
|
|
||||||
if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
|
if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
|
||||||
// for internal redirect due to auth retry we do not have any limit
|
// for internal redirect due to auth retry we do not have any limit
|
||||||
// as we might restrict the number of times a user might retry
|
// as we might restrict the number of times a user might retry
|
||||||
@@ -6004,39 +6003,15 @@ nsresult HttpBaseChannel::CheckRedirectLimit(nsIURI* aNewURI,
|
|||||||
// https and the page answers with a redirect (meta, 302, win.location, ...)
|
// https and the page answers with a redirect (meta, 302, win.location, ...)
|
||||||
// then this method can break the cycle which causes the https-only exception
|
// then this method can break the cycle which causes the https-only exception
|
||||||
// page to appear. Note that https-first mode breaks upgrade downgrade endless
|
// page to appear. Note that https-first mode breaks upgrade downgrade endless
|
||||||
// loops within ShouldUpgradeHttpsFirstRequest because https-first does not
|
// loops within ShouldUpgradeHTTPSFirstRequest because https-first does not
|
||||||
// display an exception page but needs a soft fallback/downgrade.
|
// display an exception page but needs a soft fallback/downgrade.
|
||||||
if (nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
|
if (nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
|
||||||
mURI, aNewURI, mLoadInfo,
|
mURI, mLoadInfo,
|
||||||
{nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions::
|
{nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions::
|
||||||
EnforceForHTTPSOnlyMode})) {
|
EnforceForHTTPSOnlyMode})) {
|
||||||
// Mark that we didn't upgrade to https due to loop detection in https-only
|
|
||||||
// mode to show https-only error page. We know that we are in https-only
|
|
||||||
// mode, because we passed `EnforceForHTTPSOnlyMode` to
|
|
||||||
// `IsUpgradeDowngradeEndlessLoop`. In other words we upgrade the request
|
|
||||||
// with https-only mode, but then immediately cancel the request.
|
|
||||||
uint32_t httpsOnlyStatus = mLoadInfo->GetHttpsOnlyStatus();
|
|
||||||
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) {
|
|
||||||
httpsOnlyStatus ^= nsILoadInfo::HTTPS_ONLY_UNINITIALIZED;
|
|
||||||
httpsOnlyStatus |=
|
|
||||||
nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED;
|
|
||||||
mLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(("upgrade downgrade redirect loop!\n"));
|
LOG(("upgrade downgrade redirect loop!\n"));
|
||||||
return NS_ERROR_REDIRECT_LOOP;
|
return NS_ERROR_REDIRECT_LOOP;
|
||||||
}
|
}
|
||||||
// in case of http-first mode we want to add an exception to disable the
|
|
||||||
// upgrade behavior if we have upgrade-downgrade loop to break the loop and
|
|
||||||
// load the http request next
|
|
||||||
if (mozilla::StaticPrefs::
|
|
||||||
dom_security_https_first_add_exception_on_failiure() &&
|
|
||||||
nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
|
|
||||||
mURI, aNewURI, mLoadInfo,
|
|
||||||
{nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions::
|
|
||||||
EnforceForHTTPSFirstMode})) {
|
|
||||||
nsHTTPSOnlyUtils::AddHTTPSFirstExceptionForSession(mURI, mLoadInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -651,7 +651,7 @@ class HttpBaseChannel : public nsHashPropertyBag,
|
|||||||
static void CallTypeSniffers(void* aClosure, const uint8_t* aData,
|
static void CallTypeSniffers(void* aClosure, const uint8_t* aData,
|
||||||
uint32_t aCount);
|
uint32_t aCount);
|
||||||
|
|
||||||
nsresult CheckRedirectLimit(nsIURI* aNewURI, uint32_t aRedirectFlags) const;
|
nsresult CheckRedirectLimit(uint32_t aRedirectFlags) const;
|
||||||
|
|
||||||
bool MaybeWaitForUploadStreamNormalization(nsIStreamListener* aListener,
|
bool MaybeWaitForUploadStreamNormalization(nsIStreamListener* aListener,
|
||||||
nsISupports* aContext);
|
nsISupports* aContext);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ nsresult InterceptedHttpChannel::SetupReplacementChannel(
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = CheckRedirectLimit(aURI, aRedirectFlags);
|
rv = CheckRedirectLimit(aRedirectFlags);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// While we can't resume an synthetic response, we can still propagate
|
// While we can't resume an synthetic response, we can still propagate
|
||||||
|
|||||||
@@ -1165,7 +1165,7 @@ nsresult TRRServiceChannel::SetupReplacementChannel(nsIURI* aNewURI,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = CheckRedirectLimit(aNewURI, aRedirectFlags);
|
rv = CheckRedirectLimit(aRedirectFlags);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
|
||||||
|
|||||||
@@ -703,7 +703,23 @@ nsresult nsHttpChannel::MaybeUseHTTPSRRForUpgrade(bool aShouldUpgrade,
|
|||||||
nsAutoCString uriHost;
|
nsAutoCString uriHost;
|
||||||
mURI->GetAsciiHost(uriHost);
|
mURI->GetAsciiHost(uriHost);
|
||||||
|
|
||||||
return gHttpHandler->IsHostExcludedForHTTPSRR(uriHost);
|
if (gHttpHandler->IsHostExcludedForHTTPSRR(uriHost)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
|
||||||
|
mURI, mLoadInfo,
|
||||||
|
{nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions::
|
||||||
|
EnforceForHTTPSRR})) {
|
||||||
|
// Add the host to a excluded list because:
|
||||||
|
// 1. We don't need to do the same check again.
|
||||||
|
// 2. Other subresources in the same host will be also excluded.
|
||||||
|
gHttpHandler->ExcludeHTTPSRRHost(uriHost);
|
||||||
|
LOG(("[%p] skip HTTPS upgrade for host [%s]", this, uriHost.get()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldSkipUpgradeWithHTTPSRR()) {
|
if (shouldSkipUpgradeWithHTTPSRR()) {
|
||||||
@@ -5397,27 +5413,7 @@ nsresult nsHttpChannel::SetupReplacementChannel(nsIURI* newURI,
|
|||||||
newURI, newChannel, preserveMethod, redirectFlags);
|
newURI, newChannel, preserveMethod, redirectFlags);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
||||||
nsAutoCString uriHost;
|
rv = CheckRedirectLimit(redirectFlags);
|
||||||
mURI->GetAsciiHost(uriHost);
|
|
||||||
// disable https-rr when encountering a downgrade from https to http.
|
|
||||||
// If the host would have https-rr dns-entries, it would be misconfigured
|
|
||||||
// due to giving us mixed signals:
|
|
||||||
// 1. the signal to upgrade all http requests to https,
|
|
||||||
// 2. but also downgrading to http on https via redirects.
|
|
||||||
// Add to exclude list for that reason
|
|
||||||
if (!gHttpHandler->IsHostExcludedForHTTPSRR(uriHost) &&
|
|
||||||
nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
|
|
||||||
mURI, newURI, mLoadInfo,
|
|
||||||
{nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions::
|
|
||||||
EnforceForHTTPSRR})) {
|
|
||||||
// Add the host to a excluded list because:
|
|
||||||
// 1. We don't need to do the same check again.
|
|
||||||
// 2. Other subresources in the same host will be also excluded.
|
|
||||||
gHttpHandler->ExcludeHTTPSRRHost(uriHost);
|
|
||||||
LOG(("[%p] skip HTTPS upgrade for host [%s]", this, uriHost.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = CheckRedirectLimit(newURI, redirectFlags);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// pass on the early hint observer to be able to process `103 Early Hints`
|
// pass on the early hint observer to be able to process `103 Early Hints`
|
||||||
|
|||||||
@@ -1692,7 +1692,7 @@ function handleRequest(req, res) {
|
|||||||
} else if (u.pathname === "/redirect_to_http") {
|
} else if (u.pathname === "/redirect_to_http") {
|
||||||
res.setHeader(
|
res.setHeader(
|
||||||
"Location",
|
"Location",
|
||||||
`http://test.httpsrr.redirect.com:${u.query.port}/redirect_to_http?port=${u.query.port}`
|
`http://test.httpsrr.redirect.com:${u.query.port}/redirect_to_http`
|
||||||
);
|
);
|
||||||
res.writeHead(307);
|
res.writeHead(307);
|
||||||
res.end("");
|
res.end("");
|
||||||
|
|||||||
Reference in New Issue
Block a user