зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
2cc2e49495
Коммит
2cb5d08b1f
|
@ -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;
|
||||
}
|
Загрузка…
Ссылка в новой задаче