Bug 1625156 - Added tests for HTTPS Only Mode error page. r=nhnt11

Differential Revision: https://phabricator.services.mozilla.com/D75793
This commit is contained in:
julianwels 2020-05-26 11:45:34 +00:00
Родитель 2cc2e49495
Коммит 2cb5d08b1f
8 изменённых файлов: 508 добавлений и 0 удалений

Просмотреть файл

@ -94,6 +94,7 @@ http://sub2.test1.example.com:80 privileged
http://sub2.test2.example.com:80 privileged
http://noxul.example.com:80 privileged,noxul
http://example.net:80 privileged
http://supports-insecure.expired.example.com:80 privileged
# Used to test that clearing Service Workers for domain example.com, does not clear prefixexample.com
http://prefixexample.com:80
@ -119,6 +120,7 @@ https://mismatch.expired.example.com:443 privileged,cert=expired
https://mismatch.untrusted.example.com:443 privileged,cert=untrusted
https://untrusted-expired.example.com:443 privileged,cert=untrustedandexpired
https://mismatch.untrusted-expired.example.com:443 privileged,cert=untrustedandexpired
https://supports-insecure.expired.example.com:443 privileged,cert=expired
https://no-subject-alt-name.example.com:443 cert=noSubjectAltName
# Used for secure contexts on ip addresses, see bug 1616675. Note that

Просмотреть файл

@ -61,3 +61,7 @@ function addAutofocus(selector, position = "afterbegin") {
/* Initialize Page */
initPage();
// Dispatch this event so tests can detect that we finished loading the error page.
// We're using the same event name as neterror because BrowserTestUtils.jsm relies on that.
let event = new CustomEvent("AboutNetErrorLoad", { bubbles: true });
document.dispatchEvent(event);

Просмотреть файл

@ -6,5 +6,9 @@
JAR_MANIFESTS += ['jar.mn']
BROWSER_CHROME_MANIFESTS += [
'tests/browser/browser.ini'
]
with Files('**'):
BUG_COMPONENT = ("Firefox", "Security")

Просмотреть файл

@ -0,0 +1,7 @@
[DEFAULT]
support-files =
head.js
[browser_errorpage.js]
[browser_exception.js]
support-files =
file_upgrade_insecure_server.sjs

Просмотреть файл

@ -0,0 +1,190 @@
/* 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/. */
"use strict";
const SECURE_PAGE = "https://example.com/";
const GOOD_PAGE = "http://example.com/";
const BAD_CERT = "http://expired.example.com/";
const UNKNOWN_ISSUER = "http://self-signed.example.com/";
const { TabStateFlusher } = ChromeUtils.import(
"resource:///modules/sessionstore/TabStateFlusher.jsm"
);
add_task(async function() {
info("Check that the error pages shows up");
await Promise.all([
testPageWithURI(
GOOD_PAGE,
"Should not show error page on upgradeable website.",
false
),
testPageWithURI(
BAD_CERT,
"Should show error page on bad-certificate error.",
true
),
testPageWithURI(
UNKNOWN_ISSUER,
"Should show error page on unkown-issuer error.",
true
),
]);
});
add_task(async function() {
info("Check that the go-back button returns to previous page");
// Test with and without being in an iFrame
for (let useFrame of [false, true]) {
let tab = await openErrorPage(BAD_CERT, useFrame);
let browser = tab.linkedBrowser;
is(
browser.webNavigation.canGoBack,
false,
"!webNavigation.canGoBack should be false."
);
is(
browser.webNavigation.canGoForward,
false,
"webNavigation.canGoForward should be false."
);
// Populate the shistory entries manually, since it happens asynchronously
// and the following tests will be too soon otherwise.
await TabStateFlusher.flush(browser);
let { entries } = JSON.parse(SessionStore.getTabState(tab));
is(entries.length, 1, "There should be 1 shistory entry.");
let bc = browser.browsingContext;
if (useFrame) {
bc = bc.children[0];
}
if (useFrame) {
await SpecialPowers.spawn(bc, [], async function() {
let returnButton = content.document.getElementById("goBack");
is(
returnButton,
null,
"Return-button should not be present in iFrame."
);
});
} else {
let locationChangePromise = BrowserTestUtils.waitForLocationChange(
gBrowser,
"about:home"
);
await SpecialPowers.spawn(bc, [], async function() {
let returnButton = content.document.getElementById("goBack");
is(
returnButton.getAttribute("autofocus"),
"true",
"Return-button should have focus."
);
returnButton.click();
});
await locationChangePromise;
is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
is(
browser.webNavigation.canGoForward,
false,
"!webNavigation.canGoForward"
);
is(gBrowser.currentURI.spec, "about:home", "Went back");
}
BrowserTestUtils.removeTab(gBrowser.selectedTab);
}
});
add_task(async function() {
info("Check that the go-back button returns to about:home");
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, SECURE_PAGE);
let browser = gBrowser.selectedBrowser;
let errorPageLoaded = BrowserTestUtils.waitForErrorPage(browser);
BrowserTestUtils.loadURI(browser, BAD_CERT);
await errorPageLoaded;
is(
browser.webNavigation.canGoBack,
true,
"webNavigation.canGoBack should be true before navigation."
);
is(
browser.webNavigation.canGoForward,
false,
"webNavigation.canGoForward should be false before navigation."
);
// Populate the shistory entries manually, since it happens asynchronously
// and the following tests will be too soon otherwise.
await TabStateFlusher.flush(browser);
let { entries } = JSON.parse(SessionStore.getTabState(tab));
is(entries.length, 2, "There should be 1 shistory entries.");
let pageShownPromise = BrowserTestUtils.waitForContentEvent(
browser,
"pageshow",
true
);
// Click on "go back" Button
await SpecialPowers.spawn(browser, [], async function() {
let returnButton = content.document.getElementById("goBack");
returnButton.click();
});
await pageShownPromise;
is(
browser.webNavigation.canGoBack,
false,
"webNavigation.canGoBack should be false after navigation."
);
is(
browser.webNavigation.canGoForward,
true,
"webNavigation.canGoForward should be true after navigation."
);
is(
gBrowser.currentURI.spec,
SECURE_PAGE,
"Should go back to previous page after button click."
);
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// Utils
async function testPageWithURI(uri, message, expect) {
// Open new Tab with URI
let tab;
if (expect) {
tab = await openErrorPage(uri, false);
} else {
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, uri, true);
}
// Check if HTTPS-Only Error-Page loaded instead
let browser = tab.linkedBrowser;
await SpecialPowers.spawn(browser, [message, expect], function(
message,
expect
) {
const doc = content.document;
let result = doc.documentURI.startsWith("about:httpsonlyerror");
is(result, expect, message);
});
// Close tab again
BrowserTestUtils.removeTab(tab);
}

Просмотреть файл

@ -0,0 +1,130 @@
/* 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/. */
"use strict";
const ROOT_PATH = getRootDirectory(gTestPath);
const EXPIRED_ROOT_PATH = ROOT_PATH.replace(
"chrome://mochitests/content",
"http://supports-insecure.expired.example.com"
);
const SECURE_ROOT_PATH = ROOT_PATH.replace(
"chrome://mochitests/content",
"http://example.com"
);
const INSECURE_ROOT_PATH = ROOT_PATH.replace(
"chrome://mochitests/content",
"http://example.com"
);
// This is how this test works:
//
// +-[REQUEST] http://file_upgrade_insecure_server.sjs?content
// | -> Internal HTTPS redirect
// |
// +>[RESPONSE] Expired Certificate Response
// -> HTTPS-Only Mode Error Page shows up
// -> Click exception button
//
// +----[REQUEST] https://file_upgrade_insecure_server.sjs?queryresult
// |
// | +-[REQUEST] http://file_upgrade_insecure_server.sjs?content
// | |
// | +>[RESPONSE] Webpage with a bunch of sub-resources
// | -> http://file_upgrade_insecure_ser^er.sjs?img
// | -> http://file_upgrade_insecure_server.sjs?xhr
// | -> http://file_upgrade_insecure_server.sjs?iframe
// | -> etc.
// |
// +--->[RESPONSE] List of all recorded requests and whether they were loaded
// with HTTP or not (eg.: img-ok, xhr-ok, iframe-error, ...)
add_task(async function() {
// Call sjs-file with setup query-string and store promise
let expectedQueries = new Set([
"content",
"img",
"iframe",
"xhr",
"nestedimg",
]);
// Create new tab with sjs-file requesting content.
// "supports-insecure.expired.example.com" responds to http and https but
// with an expired certificate
let tab = await openErrorPage(
`${EXPIRED_ROOT_PATH}file_upgrade_insecure_server.sjs?content`,
false /*use frame*/
);
let browser = tab.linkedBrowser;
let pageShownPromise = BrowserTestUtils.waitForContentEvent(
browser,
"pageshow",
true
);
const filesLoaded = setupFileServer();
// Since we don't know when the server has saved all it's variables,
// let's wait a bit before reloading the page.
await new Promise(resolve => executeSoon(resolve));
// click on exception-button and wait for page to load
await SpecialPowers.spawn(browser, [], async function() {
let openInsecureButton = content.document.getElementById("openInsecure");
ok(openInsecureButton != null, "openInsecureButton should exist.");
openInsecureButton.click();
});
await pageShownPromise;
// Check if the original page got loaded with http this time
await SpecialPowers.spawn(browser, [], async function() {
let doc = content.document;
ok(
!doc.documentURI.startsWith("http://expired.example.com"),
"Page should load with after exception button was clicked."
);
});
// Wait for initial sjs request to resolve
let results = await filesLoaded;
for (let resultIndex in results) {
const response = results[resultIndex];
// A response looks either like this "iframe-ok" or "[key]-[result]"
const [key, result] = response.split("-", 2);
// try to find the expected result within the results array
if (expectedQueries.has(key)) {
expectedQueries.delete(key);
is(result, "ok", `Request '${key}' should be loaded with HTTP.'`);
} else {
ok(false, `Unexpected response from server (${response})`);
}
}
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
function setupFileServer() {
// We initialize the upgrade-server with the queryresult query-string.
// We'll get a response once all files have been requested and then
// can see if they have been requested with http.
return new Promise((resolve, reject) => {
var xhrRequest = new XMLHttpRequest();
xhrRequest.open(
"GET",
`${SECURE_ROOT_PATH}file_upgrade_insecure_server.sjs?queryresult=${INSECURE_ROOT_PATH}`
);
xhrRequest.onload = function(e) {
var results = xhrRequest.responseText.split(",");
resolve(results);
};
xhrRequest.onerror = e => {
ok(false, "Could not query results from server (" + e.message + ")");
reject();
};
xhrRequest.send();
});
}

Просмотреть файл

@ -0,0 +1,108 @@
// Serverside Javascript for browser_exception.js
// Bug 1625156 - Error page for HTTPS Only Mode
const expectedQueries = ["content", "img", "iframe", "xhr", "nestedimg"];
const TOTAL_EXPECTED_REQUESTS = expectedQueries.length;
const CONTENT = path => `
<!DOCTYPE HTML>
<html>
<head>
<meta charset='utf-8'>
</head>
<body>
<p>Nested insecure website</p>
<script type="application/javascript">
var myXHR = new XMLHttpRequest();
myXHR.open("GET", "${path}file_upgrade_insecure_server.sjs?xhr");
myXHR.send(null);
</script>
<img src='${path}file_upgrade_insecure_server.sjs?img'></img>
<iframe src="${path}file_upgrade_insecure_server.sjs?iframe"></iframe>
</body>
</html>`;
const IFRAME_CONTENT = path => `
<!DOCTYPE HTML>
<html>
<head>
<meta charset='utf-8'>
</head>
<body>
<p>Insecure website</p>
<img src='${path}file_upgrade_insecure_server.sjs?nestedimg'></img>
</body>
</html>`;
function handleRequest(request, response) {
// avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
var queryString = request.queryString;
// initialize server variables and save the object state
// of the initial request, which returns async once the
// server has processed all requests.
if (queryString.startsWith("queryresult")) {
response.processAsync();
setState("totaltests", TOTAL_EXPECTED_REQUESTS.toString());
setState("receivedQueries", "");
setState("rootPath", /=(.+)/.exec(queryString)[1]);
setObjectState("queryResult", response);
return;
}
// just in case error handling for unexpected queries
if (!expectedQueries.includes(queryString)) {
response.write("unexpected-response");
return;
}
// make sure all the requested queries are indeed http
const testResult =
queryString + (request.scheme == "http" ? "-ok" : "-error");
var receivedQueries = getState("receivedQueries");
// images, scripts, etc. get queried twice, do not
// confuse the server by storing the preload as
// well as the actual load. If either the preload
// or the actual load is not https, then we would
// append "-error" in the array and the test would
// fail at the end.
if (receivedQueries.includes(testResult)) {
return;
}
// append the result to the total query string array
if (receivedQueries != "") {
receivedQueries += ",";
}
receivedQueries += testResult;
setState("receivedQueries", receivedQueries);
// keep track of how many more requests the server
// is expecting
var totaltests = parseInt(getState("totaltests"));
totaltests -= 1;
setState("totaltests", totaltests.toString());
// Respond with html content
if (queryString == "content") {
response.write(CONTENT(getState("rootPath")));
} else if (queryString == "iframe") {
response.write(IFRAME_CONTENT(getState("rootPath")));
}
// if we have received all the requests, we return
// the result back.
if (totaltests == 0) {
getObjectState("queryResult", function(queryResponse) {
if (!queryResponse) {
return;
}
var receivedQueries = getState("receivedQueries");
queryResponse.write(receivedQueries);
queryResponse.finish();
});
}
}

Просмотреть файл

@ -0,0 +1,63 @@
/* eslint-env mozilla/frame-script */
// Enable HTTPS-Only Mode
add_task(async function() {
await SpecialPowers.pushPrefEnv({
set: [["dom.security.https_only_mode", true]],
});
});
// Copied from: https://searchfox.org/mozilla-central/rev/9f074fab9bf905fad62e7cc32faf121195f4ba46/browser/base/content/test/about/head.js
async function injectErrorPageFrame(tab, src, sandboxed) {
let loadedPromise = BrowserTestUtils.browserLoaded(
tab.linkedBrowser,
true,
null,
true
);
await SpecialPowers.spawn(tab.linkedBrowser, [src, sandboxed], async function(
frameSrc,
frameSandboxed
) {
let iframe = content.document.createElement("iframe");
iframe.src = frameSrc;
if (frameSandboxed) {
iframe.setAttribute("sandbox", "allow-scripts");
}
content.document.body.appendChild(iframe);
});
await loadedPromise;
}
async function openErrorPage(src, useFrame, sandboxed) {
let dummyPage =
getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://example.com"
) + "dummy_page.html";
let tab;
if (useFrame) {
info("Loading error page in an iframe");
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, dummyPage);
await injectErrorPageFrame(tab, src, sandboxed);
} else {
let ErrorPageLoaded;
tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
() => {
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, src);
let browser = gBrowser.selectedBrowser;
ErrorPageLoaded = BrowserTestUtils.waitForErrorPage(browser);
},
false
);
info("Loading and waiting for the error page");
await ErrorPageLoaded;
}
return tab;
}