494 lines
15 KiB
JavaScript
494 lines
15 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
const SSL3_PAGE = "https://ssl3.example.com/";
|
|
const TLS10_PAGE = "https://tls1.example.com/";
|
|
const TLS12_PAGE = "https://tls12.example.com/";
|
|
const TRIPLEDES_PAGE = "https://3des.example.com/";
|
|
|
|
const lazy = {};
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(
|
|
lazy,
|
|
"gDNSOverride",
|
|
"@mozilla.org/network/native-dns-override;1",
|
|
Ci.nsINativeDNSResolverOverride
|
|
);
|
|
|
|
const DES_PREF = "security.ssl3.deprecated.rsa_des_ede3_sha";
|
|
|
|
// This includes all the cipher suite prefs we have.
|
|
function resetPrefs() {
|
|
Services.prefs.clearUserPref("security.tls.version.min");
|
|
Services.prefs.clearUserPref("security.tls.version.max");
|
|
Services.prefs.clearUserPref("security.tls.version.enable-deprecated");
|
|
Services.prefs.clearUserPref("browser.fixup.alternate.enabled");
|
|
Services.prefs.clearUserPref(DES_PREF);
|
|
}
|
|
|
|
const SSL_ERROR_BASE = -0x3000;
|
|
const SSL_ERROR_NO_CYPHER_OVERLAP = SSL_ERROR_BASE + 2;
|
|
const SSL_ERROR_PROTOCOL_VERSION_ALERT = SSL_ERROR_BASE + 98;
|
|
|
|
function nssErrorToNSErrorAsString(nssError) {
|
|
let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"].getService(
|
|
Ci.nsINSSErrorsService
|
|
);
|
|
return nssErrorsService.getXPCOMFromNSSError(nssError).toString();
|
|
}
|
|
|
|
async function resetTelemetry() {
|
|
Services.telemetry.clearEvents();
|
|
await TestUtils.waitForCondition(() => {
|
|
let events = Services.telemetry.snapshotEvents(
|
|
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
|
true
|
|
).content;
|
|
return !events || !events.length;
|
|
});
|
|
}
|
|
|
|
async function checkTelemetry(errorString, nssError) {
|
|
let loadEvent = await TestUtils.waitForCondition(() => {
|
|
let events = Services.telemetry.snapshotEvents(
|
|
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
|
|
true
|
|
).content;
|
|
return events?.find(e => e[1] == "security.ui.tlserror" && e[2] == "load");
|
|
}, "recorded telemetry for the load");
|
|
loadEvent.shift();
|
|
Assert.deepEqual(loadEvent, [
|
|
"security.ui.tlserror",
|
|
"load",
|
|
"abouttlserror",
|
|
errorString,
|
|
{
|
|
is_frame: "false",
|
|
channel_status: nssErrorToNSErrorAsString(nssError),
|
|
},
|
|
]);
|
|
}
|
|
|
|
add_task(async function resetToDefaultConfig() {
|
|
info(
|
|
"Change TLS config to cause page load to fail, check that reset button is shown and that it works"
|
|
);
|
|
|
|
// Set ourselves up for a TLS error.
|
|
Services.prefs.setIntPref("security.tls.version.min", 1); // TLS 1.0
|
|
Services.prefs.setIntPref("security.tls.version.max", 1);
|
|
|
|
await resetTelemetry();
|
|
|
|
let browser;
|
|
let pageLoaded;
|
|
await BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
() => {
|
|
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, TLS12_PAGE);
|
|
browser = gBrowser.selectedBrowser;
|
|
pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
|
|
},
|
|
false
|
|
);
|
|
|
|
info("Loading and waiting for the net error");
|
|
await pageLoaded;
|
|
|
|
await checkTelemetry(
|
|
"SSL_ERROR_PROTOCOL_VERSION_ALERT",
|
|
SSL_ERROR_PROTOCOL_VERSION_ALERT
|
|
);
|
|
|
|
// Setup an observer for the target page.
|
|
const finalLoadComplete = BrowserTestUtils.browserLoaded(
|
|
browser,
|
|
false,
|
|
TLS12_PAGE
|
|
);
|
|
|
|
await SpecialPowers.spawn(browser, [], async function () {
|
|
const doc = content.document;
|
|
ok(
|
|
doc.documentURI.startsWith("about:neterror"),
|
|
"Should be showing error page"
|
|
);
|
|
|
|
const netErrorCard = await ContentTaskUtils.waitForCondition(
|
|
() => content.document.querySelector("net-error-card")?.wrappedJSObject
|
|
);
|
|
netErrorCard.advancedButton.scrollIntoView(true);
|
|
EventUtils.synthesizeMouseAtCenter(
|
|
netErrorCard.advancedButton,
|
|
{},
|
|
content
|
|
);
|
|
await ContentTaskUtils.waitForCondition(
|
|
() => ContentTaskUtils.isVisible(netErrorCard.prefResetButton),
|
|
"prefResetButton is visible"
|
|
);
|
|
|
|
if (!Services.focus.focusedElement == netErrorCard.prefResetButton) {
|
|
await ContentTaskUtils.waitForEvent(
|
|
netErrorCard.prefResetButton,
|
|
"focus"
|
|
);
|
|
}
|
|
|
|
Assert.ok(true, "prefResetButton has focus");
|
|
|
|
EventUtils.synthesizeMouseAtCenter(
|
|
netErrorCard.prefResetButton,
|
|
{},
|
|
content
|
|
);
|
|
});
|
|
|
|
info("Waiting for the page to load after the click");
|
|
await finalLoadComplete;
|
|
|
|
resetPrefs();
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|
|
|
|
add_task(async function checkLearnMoreLink() {
|
|
info("Load an unsupported TLS page and check for a learn more link");
|
|
|
|
// Set ourselves up for TLS error
|
|
Services.prefs.setIntPref("security.tls.version.min", 3);
|
|
Services.prefs.setIntPref("security.tls.version.max", 4);
|
|
|
|
await resetTelemetry();
|
|
|
|
let browser;
|
|
let pageLoaded;
|
|
await BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
() => {
|
|
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, TLS10_PAGE);
|
|
browser = gBrowser.selectedBrowser;
|
|
pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
|
|
},
|
|
false
|
|
);
|
|
|
|
info("Loading and waiting for the net error");
|
|
await pageLoaded;
|
|
|
|
await checkTelemetry(
|
|
"SSL_ERROR_PROTOCOL_VERSION_ALERT",
|
|
SSL_ERROR_PROTOCOL_VERSION_ALERT
|
|
);
|
|
|
|
const baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
|
|
|
|
await SpecialPowers.spawn(browser, [baseURL], async function (_baseURL) {
|
|
const doc = content.document;
|
|
ok(
|
|
doc.documentURI.startsWith("about:neterror"),
|
|
"Should be showing error page"
|
|
);
|
|
|
|
const netErrorCard = await ContentTaskUtils.waitForCondition(
|
|
() => content.document.querySelector("net-error-card")?.wrappedJSObject
|
|
);
|
|
netErrorCard.advancedButton.scrollIntoView(true);
|
|
EventUtils.synthesizeMouseAtCenter(
|
|
netErrorCard.advancedButton,
|
|
{},
|
|
content
|
|
);
|
|
const tlsVersionNotice = await ContentTaskUtils.waitForCondition(
|
|
() => netErrorCard.tlsNotice
|
|
);
|
|
ok(
|
|
ContentTaskUtils.isVisible(tlsVersionNotice),
|
|
"TLS version notice is visible"
|
|
);
|
|
|
|
const learnMoreLink = netErrorCard.learnMoreLink;
|
|
ok(ContentTaskUtils.isVisible(learnMoreLink), "Learn More link is visible");
|
|
is(learnMoreLink.getAttribute("href"), _baseURL + "connection-not-secure");
|
|
|
|
const titleEl = netErrorCard.errorTitle;
|
|
const actualDataL10nID = titleEl.getAttribute("data-l10n-id");
|
|
is(
|
|
actualDataL10nID,
|
|
"nssFailure2-title",
|
|
"Correct error page title is set"
|
|
);
|
|
|
|
const errorCodeEl = netErrorCard.errorIntro.children[0];
|
|
is(
|
|
errorCodeEl.getAttribute("data-l10n-id"),
|
|
"cert-error-ssl-connection-error",
|
|
"Correct error code is set"
|
|
);
|
|
});
|
|
|
|
resetPrefs();
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|
|
|
|
// When a user tries going to a host without a suffix
|
|
// and the term doesn't match a host and we are able to suggest a
|
|
// valid correction, the page should show the correction.
|
|
// e.g. http://example/example2 -> https://www.example.com/example2
|
|
add_task(async function checkDomainCorrectionReplacesLearnMoreLink() {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["browser.fixup.alternate.enabled", false]],
|
|
});
|
|
lazy.gDNSOverride.addIPOverride("www.example.com", "::1");
|
|
|
|
info("Try loading a URI that should result in an error page");
|
|
BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
|
"http://example/example2/",
|
|
false
|
|
);
|
|
|
|
info("Loading and waiting for the net error");
|
|
let browser = gBrowser.selectedBrowser;
|
|
let pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
|
|
await pageLoaded;
|
|
|
|
const baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
|
|
|
|
await SpecialPowers.spawn(browser, [baseURL], async function (_baseURL) {
|
|
const doc = content.document;
|
|
ok(
|
|
doc.documentURI.startsWith("about:neterror"),
|
|
"Should be showing error page"
|
|
);
|
|
|
|
const netErrorCard = await ContentTaskUtils.waitForCondition(
|
|
() => doc.querySelector("net-error-card")?.wrappedJSObject
|
|
);
|
|
await netErrorCard.getUpdateComplete();
|
|
|
|
const errorIntro = netErrorCard.errorIntro;
|
|
ok(ContentTaskUtils.isVisible(errorIntro), "Error intro text is visible");
|
|
|
|
let suggestionLink;
|
|
await ContentTaskUtils.waitForCondition(() => {
|
|
suggestionLink = netErrorCard.dnsSuggestion?.querySelector("a");
|
|
return (
|
|
suggestionLink?.textContent != "" &&
|
|
suggestionLink?.getAttribute("href") ===
|
|
"https://www.example.com/example2/"
|
|
);
|
|
}, "www suggestion appears in intro text with correct link");
|
|
|
|
is(
|
|
suggestionLink.getAttribute("href"),
|
|
"https://www.example.com/example2/",
|
|
"Suggestion link points to www variant"
|
|
);
|
|
|
|
const learnMoreLink = netErrorCard.learnMoreLink;
|
|
ok(learnMoreLink, "Learn more link is present");
|
|
is(
|
|
learnMoreLink.getAttribute("href"),
|
|
_baseURL + "server-not-found-connection-problem",
|
|
"Learn more link still points to SUMO page"
|
|
);
|
|
});
|
|
|
|
lazy.gDNSOverride.clearHostOverride("www.example.com");
|
|
resetPrefs();
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|
|
|
|
// When a user tries to access a non-existent domain and no domain
|
|
// suggestion is available, the learn more link should point to the
|
|
// SUMO support page for DNS troubleshooting.
|
|
add_task(async function checkDnsNotFoundLearnMoreLink() {
|
|
info("Load a non-existent domain and check the learn more link");
|
|
|
|
BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
|
"http://thisdomaindoesnotexist123456.test/",
|
|
false
|
|
);
|
|
let browser = gBrowser.selectedBrowser;
|
|
let pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
|
|
await pageLoaded;
|
|
|
|
const baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
|
|
|
|
await SpecialPowers.spawn(browser, [baseURL], async function (_baseURL) {
|
|
const doc = content.document;
|
|
|
|
const netErrorCard = await ContentTaskUtils.waitForCondition(
|
|
() => doc.querySelector("net-error-card")?.wrappedJSObject
|
|
);
|
|
|
|
let learnMoreLink;
|
|
await ContentTaskUtils.waitForCondition(() => {
|
|
learnMoreLink = netErrorCard.learnMoreLink;
|
|
return learnMoreLink && learnMoreLink.textContent != "";
|
|
}, "Learn more link has been set");
|
|
|
|
ok(ContentTaskUtils.isVisible(learnMoreLink), "Learn More link is visible");
|
|
|
|
is(
|
|
learnMoreLink.getAttribute("href"),
|
|
_baseURL + "server-not-found-connection-problem",
|
|
"Link points to SUMO DNS troubleshooting page"
|
|
);
|
|
});
|
|
|
|
BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
|
});
|
|
|
|
// Test that ciphersuites that use 3DES (namely, TLS_RSA_WITH_3DES_EDE_CBC_SHA)
|
|
// can only be enabled when deprecated TLS is enabled.
|
|
add_task(async function onlyAllow3DESWithDeprecatedTLS() {
|
|
await resetTelemetry();
|
|
|
|
// By default, connecting to a server that only uses 3DES should fail.
|
|
await BrowserTestUtils.withNewTab(
|
|
{ gBrowser, url: "about:blank" },
|
|
async browser => {
|
|
BrowserTestUtils.startLoadingURIString(browser, TRIPLEDES_PAGE);
|
|
await BrowserTestUtils.waitForErrorPage(browser);
|
|
}
|
|
);
|
|
|
|
await checkTelemetry(
|
|
"SSL_ERROR_NO_CYPHER_OVERLAP",
|
|
SSL_ERROR_NO_CYPHER_OVERLAP
|
|
);
|
|
|
|
// Enabling deprecated TLS should also enable 3DES.
|
|
Services.prefs.setBoolPref("security.tls.version.enable-deprecated", true);
|
|
await BrowserTestUtils.withNewTab(
|
|
{ gBrowser, url: "about:blank" },
|
|
async browser => {
|
|
BrowserTestUtils.startLoadingURIString(browser, TRIPLEDES_PAGE);
|
|
await BrowserTestUtils.browserLoaded(browser, false, TRIPLEDES_PAGE);
|
|
}
|
|
);
|
|
|
|
for (let feltPrivacy of [true, false]) {
|
|
// 3DES can be disabled separately.
|
|
Services.prefs.setBoolPref(DES_PREF, false);
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["security.certerrors.felt-privacy-v1", feltPrivacy]],
|
|
});
|
|
|
|
await BrowserTestUtils.withNewTab(
|
|
{ gBrowser, url: "about:blank" },
|
|
async browser => {
|
|
BrowserTestUtils.startLoadingURIString(browser, TRIPLEDES_PAGE);
|
|
await BrowserTestUtils.waitForErrorPage(browser);
|
|
let prefWasReset = TestUtils.waitForPrefChange(DES_PREF);
|
|
|
|
if (feltPrivacy) {
|
|
await SpecialPowers.spawn(browser, [], async function () {
|
|
const doc = content.document;
|
|
const netErrorCard =
|
|
doc.querySelector("net-error-card")?.wrappedJSObject;
|
|
Assert.ok(netErrorCard, "netErrorCard is rendered.");
|
|
|
|
netErrorCard.advancedButton.scrollIntoView();
|
|
EventUtils.synthesizeMouseAtCenter(
|
|
netErrorCard.advancedButton,
|
|
{},
|
|
content
|
|
);
|
|
await ContentTaskUtils.waitForCondition(
|
|
() => ContentTaskUtils.isVisible(netErrorCard.advancedContainer),
|
|
"Advanced container is visible"
|
|
);
|
|
|
|
const prefResetButton = netErrorCard.prefResetButton;
|
|
Assert.ok(prefResetButton, "prefResetButton exists in the DOM.");
|
|
netErrorCard.prefResetButton.scrollIntoView();
|
|
await ContentTaskUtils.waitForCondition(
|
|
() => ContentTaskUtils.isVisible(netErrorCard.prefResetButton),
|
|
"Pref reset button is visible"
|
|
);
|
|
ok(
|
|
ContentTaskUtils.isVisible(prefResetButton),
|
|
"prefResetButton is visible"
|
|
);
|
|
|
|
prefResetButton.click();
|
|
});
|
|
} else {
|
|
await SpecialPowers.spawn(browser, [], async function () {
|
|
const doc = content.document;
|
|
|
|
const prefResetButton = doc.getElementById("prefResetButton");
|
|
Assert.ok(prefResetButton, "prefResetButton exists in the DOM.");
|
|
|
|
await ContentTaskUtils.waitForCondition(
|
|
() => ContentTaskUtils.isVisible(prefResetButton),
|
|
"Pref reset button is visible"
|
|
);
|
|
ok(
|
|
ContentTaskUtils.isVisible(prefResetButton),
|
|
"prefResetButton is visible"
|
|
);
|
|
|
|
prefResetButton.click();
|
|
});
|
|
}
|
|
|
|
await prefWasReset;
|
|
}
|
|
);
|
|
}
|
|
|
|
resetPrefs();
|
|
});
|
|
|
|
add_task(async function test_tryAgainButtonAutofocus() {
|
|
Services.io.offline = true;
|
|
registerCleanupFunction(() => {
|
|
Services.io.offline = false;
|
|
});
|
|
|
|
let proxyPrefValue = SpecialPowers.getIntPref("network.proxy.type");
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [
|
|
["network.proxy.type", 0],
|
|
["browser.cache.disk.enable", false],
|
|
["browser.cache.memory.enable", false],
|
|
["security.certerrors.felt-privacy-v1", true],
|
|
],
|
|
});
|
|
|
|
await BrowserTestUtils.withNewTab("about:blank", async function (browser) {
|
|
let netErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
|
|
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
|
BrowserTestUtils.startLoadingURIString(browser, "http://example.com/");
|
|
await netErrorLoaded;
|
|
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["network.proxy.type", proxyPrefValue]],
|
|
});
|
|
|
|
await SpecialPowers.spawn(browser, [], async function () {
|
|
const netErrorCard =
|
|
content.document.querySelector("net-error-card").wrappedJSObject;
|
|
await netErrorCard.getUpdateComplete();
|
|
const tryAgainButton = netErrorCard.tryAgainButton;
|
|
Assert.ok(tryAgainButton, "tryAgainButton exists");
|
|
await tryAgainButton.updateComplete;
|
|
Assert.equal(
|
|
netErrorCard.renderRoot.activeElement,
|
|
tryAgainButton,
|
|
"tryAgainButton has focus"
|
|
);
|
|
});
|
|
});
|
|
|
|
await SpecialPowers.popPrefEnv();
|
|
});
|