forked from mirrors/gecko-dev
To support and enable the migration, quite a bit of refactoring is needed. Many of the localised error messages are in fact fragments of HTML, including messages with nesting not supported by Fluent. In the FTL, these have each been split up into multiple messages using a custom migration transform (included directly in the script). This allows for localisers to work with the messages without HTML syntax, but does require the messages' structures to be maintained elsewhere. To that effect, the JS file represents messages as arrays of `[tagName, l10nId, l10nArgs]` tuples from which it builds the messages' elements. This fixex bug 1621895. Though extensive, the refactoring done here is for the most part limited to what's required by the Fluent migration. For instance, not all issues raised in bug 1722896 are resolved here. Places where the structure was sufficiently messy to have introduced bugs or dead code have been cleaned up a bit, though. This variant of netError that's used by the browser is not itself overridden by anyone else, which allows for it to be tackled first and independently of the docshell and mobile variants. As a part of its content is still passed in as a query parameter, it's possible that later refactors of the rest of the netError system will allow for further clean-up here. Differential Revision: https://phabricator.services.mozilla.com/D155951
574 lines
19 KiB
JavaScript
574 lines
19 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
// This is testing the aboutCertError page (Bug 1207107).
|
|
|
|
const GOOD_PAGE = "https://example.com/";
|
|
const GOOD_PAGE_2 = "https://example.org/";
|
|
const BAD_CERT = "https://expired.example.com/";
|
|
const UNKNOWN_ISSUER = "https://self-signed.example.com ";
|
|
const BAD_STS_CERT =
|
|
"https://badchain.include-subdomains.pinning.example.com:443";
|
|
const { TabStateFlusher } = ChromeUtils.import(
|
|
"resource:///modules/sessionstore/TabStateFlusher.jsm"
|
|
);
|
|
|
|
add_task(async function checkReturnToAboutHome() {
|
|
info(
|
|
"Loading a bad cert page directly and making sure 'return to previous page' goes to about:home"
|
|
);
|
|
for (let useFrame of [false, true]) {
|
|
let tab = await openErrorPage(BAD_CERT, useFrame);
|
|
let browser = tab.linkedBrowser;
|
|
|
|
is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
|
|
is(
|
|
browser.webNavigation.canGoForward,
|
|
false,
|
|
"!webNavigation.canGoForward"
|
|
);
|
|
|
|
// 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 is one shistory entry");
|
|
|
|
info("Clicking the go back button on about:certerror");
|
|
let bc = browser.browsingContext;
|
|
if (useFrame) {
|
|
bc = bc.children[0];
|
|
}
|
|
let locationChangePromise = BrowserTestUtils.waitForLocationChange(
|
|
gBrowser,
|
|
"about:home"
|
|
);
|
|
await SpecialPowers.spawn(bc, [useFrame], async function(subFrame) {
|
|
let returnButton = content.document.getElementById("returnButton");
|
|
if (!subFrame) {
|
|
if (!Services.focus.focusedElement == returnButton) {
|
|
await ContentTaskUtils.waitForEvent(returnButton, "focus");
|
|
}
|
|
Assert.ok(true, "returnButton has focus");
|
|
}
|
|
// Note that going back to about:newtab might cause a process flip, if
|
|
// the browser is configured to run about:newtab in its own special
|
|
// content process.
|
|
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 checkReturnToPreviousPage() {
|
|
info(
|
|
"Loading a bad cert page and making sure 'return to previous page' goes back"
|
|
);
|
|
for (let useFrame of [false, true]) {
|
|
let tab;
|
|
let browser;
|
|
if (useFrame) {
|
|
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
|
|
browser = tab.linkedBrowser;
|
|
|
|
BrowserTestUtils.loadURI(browser, GOOD_PAGE_2);
|
|
await BrowserTestUtils.browserLoaded(browser, false, GOOD_PAGE_2);
|
|
await injectErrorPageFrame(tab, BAD_CERT);
|
|
} else {
|
|
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
|
|
browser = gBrowser.selectedBrowser;
|
|
|
|
info("Loading and waiting for the cert error");
|
|
let certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
|
|
BrowserTestUtils.loadURI(browser, BAD_CERT);
|
|
await certErrorLoaded;
|
|
}
|
|
|
|
is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
|
|
is(
|
|
browser.webNavigation.canGoForward,
|
|
false,
|
|
"!webNavigation.canGoForward"
|
|
);
|
|
|
|
// 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 are two shistory entries");
|
|
|
|
info("Clicking the go back button on about:certerror");
|
|
let bc = browser.browsingContext;
|
|
if (useFrame) {
|
|
bc = bc.children[0];
|
|
}
|
|
|
|
let pageShownPromise = BrowserTestUtils.waitForContentEvent(
|
|
browser,
|
|
"pageshow",
|
|
true
|
|
);
|
|
await SpecialPowers.spawn(bc, [useFrame], async function(subFrame) {
|
|
let returnButton = content.document.getElementById("returnButton");
|
|
returnButton.click();
|
|
});
|
|
await pageShownPromise;
|
|
|
|
is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
|
|
is(browser.webNavigation.canGoForward, true, "webNavigation.canGoForward");
|
|
is(gBrowser.currentURI.spec, GOOD_PAGE, "Went back");
|
|
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
}
|
|
});
|
|
|
|
// This checks that the appinfo.appBuildID starts with a date string,
|
|
// which is required for the misconfigured system time check.
|
|
add_task(async function checkAppBuildIDIsDate() {
|
|
let appBuildID = Services.appinfo.appBuildID;
|
|
let year = parseInt(appBuildID.substr(0, 4), 10);
|
|
let month = parseInt(appBuildID.substr(4, 2), 10);
|
|
let day = parseInt(appBuildID.substr(6, 2), 10);
|
|
|
|
ok(year >= 2016 && year <= 2100, "appBuildID contains a valid year");
|
|
ok(month >= 1 && month <= 12, "appBuildID contains a valid month");
|
|
ok(day >= 1 && day <= 31, "appBuildID contains a valid day");
|
|
});
|
|
|
|
add_task(async function checkAdvancedDetails() {
|
|
info(
|
|
"Loading a bad cert page and verifying the main error and advanced details section"
|
|
);
|
|
for (let useFrame of [false, true]) {
|
|
let tab = await openErrorPage(BAD_CERT, useFrame);
|
|
let browser = tab.linkedBrowser;
|
|
|
|
let bc = browser.browsingContext;
|
|
if (useFrame) {
|
|
bc = bc.children[0];
|
|
}
|
|
|
|
let message = await SpecialPowers.spawn(bc, [], async function() {
|
|
let doc = content.document;
|
|
|
|
// Wait until fluent sets the error description.
|
|
let shortDescText;
|
|
await ContentTaskUtils.waitForCondition(() => {
|
|
shortDescText = doc.getElementById("errorShortDescText");
|
|
return shortDescText.textContent != "";
|
|
}, "error description has been set");
|
|
|
|
Assert.ok(
|
|
shortDescText.textContent.includes("expired.example.com"),
|
|
"Should list hostname in error message."
|
|
);
|
|
Assert.ok(
|
|
doc.getElementById("certificateErrorDebugInformation").hidden,
|
|
"Debug info is initially hidden"
|
|
);
|
|
|
|
let exceptionButton = doc.getElementById("exceptionDialogButton");
|
|
Assert.ok(
|
|
!exceptionButton.disabled,
|
|
"Exception button is not disabled by default."
|
|
);
|
|
|
|
let advancedButton = doc.getElementById("advancedButton");
|
|
advancedButton.click();
|
|
|
|
// Wait until fluent sets the errorCode inner text.
|
|
let el;
|
|
await ContentTaskUtils.waitForCondition(() => {
|
|
el = doc.getElementById("errorCode");
|
|
return el.textContent != "";
|
|
}, "error code has been set inside the advanced button panel");
|
|
|
|
return { textContent: el.textContent, tagName: el.tagName };
|
|
});
|
|
is(
|
|
message.textContent,
|
|
"SEC_ERROR_EXPIRED_CERTIFICATE",
|
|
"Correct error message found"
|
|
);
|
|
is(message.tagName, "a", "Error message is a link");
|
|
|
|
message = await SpecialPowers.spawn(bc, [], async function() {
|
|
let doc = content.document;
|
|
let errorCode = doc.getElementById("errorCode");
|
|
errorCode.click();
|
|
let div = doc.getElementById("certificateErrorDebugInformation");
|
|
let text = doc.getElementById("certificateErrorText");
|
|
Assert.ok(
|
|
content.getComputedStyle(div).display !== "none",
|
|
"Debug information is visible"
|
|
);
|
|
let failedCertChain = content.docShell.failedChannel.securityInfo.failedCertChain.map(
|
|
cert => cert.getBase64DERString()
|
|
);
|
|
return {
|
|
divDisplay: content.getComputedStyle(div).display,
|
|
text: text.textContent,
|
|
failedCertChain,
|
|
};
|
|
});
|
|
isnot(message.divDisplay, "none", "Debug information is visible");
|
|
ok(message.text.includes(BAD_CERT), "Correct URL found");
|
|
ok(
|
|
message.text.includes("Certificate has expired"),
|
|
"Correct error message found"
|
|
);
|
|
ok(
|
|
message.text.includes("HTTP Strict Transport Security: false"),
|
|
"Correct HSTS value found"
|
|
);
|
|
ok(
|
|
message.text.includes("HTTP Public Key Pinning: false"),
|
|
"Correct HPKP value found"
|
|
);
|
|
let certChain = getCertChainAsString(message.failedCertChain);
|
|
ok(message.text.includes(certChain), "Found certificate chain");
|
|
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
}
|
|
});
|
|
|
|
add_task(async function checkAdvancedDetailsForHSTS() {
|
|
info(
|
|
"Loading a bad STS cert page and verifying the advanced details section"
|
|
);
|
|
for (let useFrame of [false, true]) {
|
|
let tab = await openErrorPage(BAD_STS_CERT, useFrame);
|
|
let browser = tab.linkedBrowser;
|
|
|
|
let bc = browser.browsingContext;
|
|
if (useFrame) {
|
|
bc = bc.children[0];
|
|
}
|
|
|
|
let message = await SpecialPowers.spawn(bc, [], async function() {
|
|
let doc = content.document;
|
|
let advancedButton = doc.getElementById("advancedButton");
|
|
advancedButton.click();
|
|
|
|
// Wait until fluent sets the errorCode inner text.
|
|
let ec;
|
|
await ContentTaskUtils.waitForCondition(() => {
|
|
ec = doc.getElementById("errorCode");
|
|
return ec.textContent != "";
|
|
}, "error code has been set inside the advanced button panel");
|
|
|
|
let cdl = doc.getElementById("cert_domain_link");
|
|
return {
|
|
ecTextContent: ec.textContent,
|
|
ecTagName: ec.tagName,
|
|
cdlTextContent: cdl.textContent,
|
|
cdlTagName: cdl.tagName,
|
|
};
|
|
});
|
|
|
|
const badStsUri = Services.io.newURI(BAD_STS_CERT);
|
|
is(
|
|
message.ecTextContent,
|
|
"SSL_ERROR_BAD_CERT_DOMAIN",
|
|
"Correct error message found"
|
|
);
|
|
is(message.ecTagName, "a", "Error message is a link");
|
|
const url = badStsUri.prePath.slice(badStsUri.prePath.indexOf(".") + 1);
|
|
is(message.cdlTextContent, url, "Correct cert_domain_link contents found");
|
|
is(message.cdlTagName, "a", "cert_domain_link is a link");
|
|
|
|
message = await SpecialPowers.spawn(bc, [], async function() {
|
|
let doc = content.document;
|
|
let errorCode = doc.getElementById("errorCode");
|
|
errorCode.click();
|
|
let div = doc.getElementById("certificateErrorDebugInformation");
|
|
let text = doc.getElementById("certificateErrorText");
|
|
let failedCertChain = content.docShell.failedChannel.securityInfo.failedCertChain.map(
|
|
cert => cert.getBase64DERString()
|
|
);
|
|
return {
|
|
divDisplay: content.getComputedStyle(div).display,
|
|
text: text.textContent,
|
|
failedCertChain,
|
|
};
|
|
});
|
|
isnot(message.divDisplay, "none", "Debug information is visible");
|
|
ok(message.text.includes(badStsUri.spec), "Correct URL found");
|
|
ok(
|
|
message.text.includes(
|
|
"requested domain name does not match the server\u2019s certificate"
|
|
),
|
|
"Correct error message found"
|
|
);
|
|
ok(
|
|
message.text.includes("HTTP Strict Transport Security: false"),
|
|
"Correct HSTS value found"
|
|
);
|
|
ok(
|
|
message.text.includes("HTTP Public Key Pinning: true"),
|
|
"Correct HPKP value found"
|
|
);
|
|
let certChain = getCertChainAsString(message.failedCertChain);
|
|
ok(message.text.includes(certChain), "Found certificate chain");
|
|
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
}
|
|
});
|
|
|
|
add_task(async function checkUnknownIssuerLearnMoreLink() {
|
|
info(
|
|
"Loading a cert error for self-signed pages and checking the correct link is shown"
|
|
);
|
|
for (let useFrame of [false, true]) {
|
|
let tab = await openErrorPage(UNKNOWN_ISSUER, useFrame);
|
|
let browser = tab.linkedBrowser;
|
|
|
|
let bc = browser.browsingContext;
|
|
if (useFrame) {
|
|
bc = bc.children[0];
|
|
}
|
|
|
|
let href = await SpecialPowers.spawn(bc, [], async function() {
|
|
let learnMoreLink = content.document.getElementById("learnMoreLink");
|
|
return learnMoreLink.href;
|
|
});
|
|
ok(href.endsWith("security-error"), "security-error in the Learn More URL");
|
|
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
}
|
|
});
|
|
|
|
add_task(async function checkViewCertificate() {
|
|
info("Loading a cert error and checking that the certificate can be shown.");
|
|
for (let useFrame of [true, false]) {
|
|
if (useFrame) {
|
|
// Bug #1573502
|
|
continue;
|
|
}
|
|
let tab = await openErrorPage(UNKNOWN_ISSUER, useFrame);
|
|
let browser = tab.linkedBrowser;
|
|
|
|
let bc = browser.browsingContext;
|
|
if (useFrame) {
|
|
bc = bc.children[0];
|
|
}
|
|
|
|
let loaded = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
|
|
await SpecialPowers.spawn(bc, [], async function() {
|
|
let viewCertificate = content.document.getElementById("viewCertificate");
|
|
viewCertificate.click();
|
|
});
|
|
await loaded;
|
|
|
|
let spec = gBrowser.selectedTab.linkedBrowser.documentURI.spec;
|
|
Assert.ok(
|
|
spec.startsWith("about:certificate"),
|
|
"about:certificate is the new opened tab"
|
|
);
|
|
|
|
await SpecialPowers.spawn(
|
|
gBrowser.selectedTab.linkedBrowser,
|
|
[],
|
|
async function() {
|
|
let doc = content.document;
|
|
let certificateSection = await ContentTaskUtils.waitForCondition(() => {
|
|
return doc.querySelector("certificate-section");
|
|
}, "Certificate section found");
|
|
|
|
let infoGroup = certificateSection.shadowRoot.querySelector(
|
|
"info-group"
|
|
);
|
|
Assert.ok(infoGroup, "infoGroup found");
|
|
|
|
let items = infoGroup.shadowRoot.querySelectorAll("info-item");
|
|
let commonnameID = items[items.length - 1].shadowRoot
|
|
.querySelector("label")
|
|
.getAttribute("data-l10n-id");
|
|
Assert.equal(
|
|
commonnameID,
|
|
"certificate-viewer-common-name",
|
|
"The correct item was selected"
|
|
);
|
|
|
|
let commonnameValue = items[items.length - 1].shadowRoot.querySelector(
|
|
".info"
|
|
).textContent;
|
|
Assert.equal(
|
|
commonnameValue,
|
|
"self-signed.example.com",
|
|
"Shows the correct certificate in the page"
|
|
);
|
|
}
|
|
);
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab); // closes about:certificate
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
}
|
|
});
|
|
|
|
add_task(async function checkBadStsCertHeadline() {
|
|
info(
|
|
"Loading a bad sts cert error page and checking that the correct headline is shown"
|
|
);
|
|
for (let useFrame of [false, true]) {
|
|
let tab = await openErrorPage(BAD_CERT, useFrame);
|
|
let browser = tab.linkedBrowser;
|
|
|
|
let bc = browser.browsingContext;
|
|
if (useFrame) {
|
|
bc = bc.children[0];
|
|
}
|
|
|
|
await SpecialPowers.spawn(bc, [useFrame], async _useFrame => {
|
|
let titleText = content.document.querySelector(".title-text");
|
|
await ContentTaskUtils.waitForCondition(
|
|
() => titleText.textContent,
|
|
"Error page title is initialized"
|
|
);
|
|
let titleContent = titleText.textContent;
|
|
if (_useFrame) {
|
|
ok(
|
|
titleContent.endsWith("Security Issue"),
|
|
"Did Not Connect: Potential Security Issue"
|
|
);
|
|
} else {
|
|
ok(
|
|
titleContent.endsWith("Risk Ahead"),
|
|
"Warning: Potential Security Risk Ahead"
|
|
);
|
|
}
|
|
});
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
}
|
|
});
|
|
|
|
add_task(async function checkSandboxedIframe() {
|
|
info(
|
|
"Loading a bad sts cert error in a sandboxed iframe and check that the correct headline is shown"
|
|
);
|
|
let useFrame = true;
|
|
let sandboxed = true;
|
|
let tab = await openErrorPage(BAD_CERT, useFrame, sandboxed);
|
|
let browser = tab.linkedBrowser;
|
|
|
|
let bc = browser.browsingContext.children[0];
|
|
await SpecialPowers.spawn(bc, [], async function() {
|
|
let doc = content.document;
|
|
|
|
// aboutNetError.js is using async localization to format several messages
|
|
// and in result the translation may be applied later.
|
|
// We want to return the textContent of the element only after
|
|
// the translation completes, so let's wait for it here.
|
|
await ContentTaskUtils.waitForCondition(() => {
|
|
let elements = [
|
|
doc.querySelector(".title-text"),
|
|
doc.getElementById("errorCode"),
|
|
];
|
|
|
|
return elements.every(elem => !!elem.textContent.trim().length);
|
|
});
|
|
|
|
let titleText = doc.querySelector(".title-text");
|
|
Assert.ok(
|
|
titleText.textContent.endsWith("Security Issue"),
|
|
"Title shows Did Not Connect: Potential Security Issue"
|
|
);
|
|
|
|
let el = doc.getElementById("errorCode");
|
|
|
|
Assert.equal(
|
|
el.textContent,
|
|
"SEC_ERROR_EXPIRED_CERTIFICATE",
|
|
"Correct error message found"
|
|
);
|
|
Assert.equal(el.tagName, "a", "Error message is a link");
|
|
});
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|
|
|
|
add_task(async function checkViewSource() {
|
|
info(
|
|
"Loading a bad sts cert error in a sandboxed iframe and check that the correct headline is shown"
|
|
);
|
|
let uri = "view-source:" + BAD_CERT;
|
|
let tab = await openErrorPage(uri);
|
|
let browser = tab.linkedBrowser;
|
|
|
|
await SpecialPowers.spawn(browser, [], async function() {
|
|
let doc = content.document;
|
|
|
|
// Wait until fluent sets the error code and description.
|
|
let errorCode;
|
|
let shortDescText;
|
|
await ContentTaskUtils.waitForCondition(() => {
|
|
errorCode = doc.getElementById("errorCode");
|
|
shortDescText = doc.getElementById("errorShortDescText");
|
|
return errorCode.textContent != "" && shortDescText.textContent != "";
|
|
}, "error code and description have been set");
|
|
Assert.equal(
|
|
errorCode.textContent,
|
|
"SEC_ERROR_EXPIRED_CERTIFICATE",
|
|
"Correct error message found"
|
|
);
|
|
Assert.equal(errorCode.tagName, "a", "Error message is a link");
|
|
|
|
let titleText = doc.querySelector(".title-text");
|
|
Assert.equal(
|
|
titleText.textContent,
|
|
"Warning: Potential Security Risk Ahead"
|
|
);
|
|
|
|
Assert.ok(
|
|
shortDescText.textContent.includes("expired.example.com"),
|
|
"Should list hostname in error message."
|
|
);
|
|
|
|
let whatToDoText = doc.getElementById("errorWhatToDoText");
|
|
Assert.ok(
|
|
whatToDoText.textContent.includes("expired.example.com"),
|
|
"Should list hostname in what to do text."
|
|
);
|
|
});
|
|
|
|
let loaded = BrowserTestUtils.browserLoaded(browser, false, uri);
|
|
info("Clicking the exceptionDialogButton in advanced panel");
|
|
await SpecialPowers.spawn(browser, [], async function() {
|
|
let doc = content.document;
|
|
let exceptionButton = doc.getElementById("exceptionDialogButton");
|
|
exceptionButton.click();
|
|
});
|
|
|
|
info("Loading the url after adding exception");
|
|
await loaded;
|
|
|
|
await SpecialPowers.spawn(browser, [], async function() {
|
|
let doc = content.document;
|
|
ok(
|
|
!doc.documentURI.startsWith("about:certerror"),
|
|
"Exception has been added"
|
|
);
|
|
});
|
|
|
|
let certOverrideService = Cc[
|
|
"@mozilla.org/security/certoverride;1"
|
|
].getService(Ci.nsICertOverrideService);
|
|
certOverrideService.clearValidityOverride("expired.example.com", -1, {});
|
|
|
|
loaded = BrowserTestUtils.waitForErrorPage(browser);
|
|
BrowserReloadSkipCache();
|
|
await loaded;
|
|
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|