tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit f4b250cea88b2860774ac44f434f6e9c9f1134f6
parent cac86b578ebe55a63f2f3792f4b14b193b028b73
Author: Jack Brown <jbrown@mozilla.com>
Date:   Wed,  3 Dec 2025 21:29:15 +0000

Bug 1991885 - Content for SEC_ERROR_REVOKED_CERTIFICATE error page. r=niklas,fluent-reviewers,bolsson

Differential Revision: https://phabricator.services.mozilla.com/D273016

Diffstat:
Mbrowser/base/content/test/about/browser.toml | 2++
Abrowser/base/content/test/about/browser_aboutCertError_revoked.js | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/content/aboutNetError.mjs | 31+------------------------------
Mtoolkit/content/aboutNetErrorHelpers.mjs | 30++++++++++++++++++++++++++++++
Mtoolkit/content/net-error-card.mjs | 23++++++++++++++++++++++-
Mtoolkit/locales/en-US/toolkit/neterror/certError.ftl | 7+++++++
6 files changed, 167 insertions(+), 31 deletions(-)

diff --git a/browser/base/content/test/about/browser.toml b/browser/base/content/test/about/browser.toml @@ -36,6 +36,8 @@ support-files = [ ["browser_aboutCertError_offlineSupport_feltPrivacyV1.js"] +["browser_aboutCertError_revoked.js"] + ["browser_aboutCertError_telemetry.js"] ["browser_aboutDialog_distribution.js"] diff --git a/browser/base/content/test/about/browser_aboutCertError_revoked.js b/browser/base/content/test/about/browser_aboutCertError_revoked.js @@ -0,0 +1,105 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const BAD_CERT = "https://expired.example.com/"; +const REVOKED_CERT_PATH = + "../../../../../security/manager/ssl/tests/mochitest/browser/revoked.pem"; + +function pemToBase64(pem) { + return pem + .replace(/-----BEGIN CERTIFICATE-----/, "") + .replace(/-----END CERTIFICATE-----/, "") + .replace(/\s+/g, ""); +} + +// The revoked certificate page should not offer a proceed button. +add_task(async function checkRevokedCertificateAdvancedCopy() { + await setSecurityCertErrorsFeltPrivacyToTrue(); + + const revokedPem = await IOUtils.readUTF8(getTestFilePath(REVOKED_CERT_PATH)); + const revokedCertBase64 = pemToBase64(revokedPem); + + const tab = await openErrorPage(BAD_CERT); + const browser = tab.linkedBrowser; + + await SpecialPowers.spawn( + browser, + [revokedCertBase64], + async function (revokedCert_Base64) { + const mockErrorInfo = { + errorCodeString: "SEC_ERROR_REVOKED_CERTIFICATE", + errorIsOverridable: false, + channelStatus: 0, + overridableErrorCategory: "trust-error", + validNotBefore: Date.now() - 1000, + validNotAfter: Date.now() + 1000, + certValidityRangeNotAfter: Date.now() + 1000, + certValidityRangeNotBefore: Date.now() - 1000, + issuerCommonName: "ca", + errorMessage: "Peer's Certificate has been revoked.", + hasHSTS: false, + hasHPKP: false, + certChainStrings: [revokedCert_Base64], + }; + + content.document.getFailedCertSecurityInfo = () => mockErrorInfo; + + const netErrorCard = + content.document.querySelector("net-error-card").wrappedJSObject; + const info = Cu.cloneInto(mockErrorInfo, netErrorCard); + netErrorCard.errorInfo = info; + netErrorCard.hostname = "revoked.example.com"; + netErrorCard.domainMismatchNames = null; + netErrorCard.domainMismatchNamesPromise = null; + netErrorCard.certificateErrorText = null; + netErrorCard.certificateErrorTextPromise = null; + netErrorCard.hideExceptionButton = netErrorCard.shouldHideExceptionButton( + info.errorCodeString + ); + netErrorCard.requestUpdate(); + await netErrorCard.getUpdateComplete(); + + const advancedButton = netErrorCard.advancedButton; + advancedButton.scrollIntoView(true); + EventUtils.synthesizeMouseAtCenter(advancedButton, {}, content); + + await ContentTaskUtils.waitForCondition( + () => netErrorCard.advancedContainer, + "Advanced section should be rendered for revoked certificate" + ); + await ContentTaskUtils.waitForCondition( + () => netErrorCard.whyDangerous && netErrorCard.whatCanYouDo, + "Revoked copy should be rendered" + ); + + Assert.ok( + netErrorCard.advancedShowing, + "Advanced details are shown for revoked certificates." + ); + Assert.ok( + !netErrorCard.exceptionButton, + "Proceed button should not be shown for revoked certificates." + ); + Assert.equal( + netErrorCard.whyDangerous.dataset.l10nId, + "fp-certerror-revoked-why-dangerous-body", + "Using the 'revoked' variant of the 'Why Dangerous' copy." + ); + Assert.equal( + netErrorCard.whatCanYouDo.dataset.l10nId, + "fp-certerror-revoked-what-can-you-do-body", + "Using the 'revoked' variant of the 'What can you do' copy." + ); + Assert.equal( + netErrorCard.learnMoreLink.getAttribute("support-page"), + "connection-not-secure", + "'Learn more' link points to the standard support page." + ); + } + ); + + BrowserTestUtils.removeTab(tab); + await SpecialPowers.popPrefEnv(); +}); diff --git a/toolkit/content/aboutNetError.mjs b/toolkit/content/aboutNetError.mjs @@ -19,6 +19,7 @@ import { gNoConnectivity, gOffline, retryThis, + errorHasNoUserFix, } from "chrome://global/content/aboutNetErrorHelpers.mjs"; const formatter = new Intl.DateTimeFormat(); @@ -1184,36 +1185,6 @@ function setCertErrorDetails() { } } -// Returns true if the error identified by the given error code string has no -// particular action the user can take to fix it. -function errorHasNoUserFix(errorCodeString) { - switch (errorCodeString) { - case "MOZILLA_PKIX_ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY": - case "MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING": - case "MOZILLA_PKIX_ERROR_ISSUER_NO_LONGER_TRUSTED": - case "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE": - case "MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH": - case "SEC_ERROR_BAD_DER": - case "SEC_ERROR_BAD_SIGNATURE": - case "SEC_ERROR_CERT_NOT_IN_NAME_SPACE": - case "SEC_ERROR_EXTENSION_VALUE_INVALID": - case "SEC_ERROR_INADEQUATE_CERT_TYPE": - case "SEC_ERROR_INADEQUATE_KEY_USAGE": - case "SEC_ERROR_INVALID_KEY": - case "SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID": - case "SEC_ERROR_REVOKED_CERTIFICATE": - case "SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION": - case "SEC_ERROR_UNSUPPORTED_EC_POINT_FORM": - case "SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE": - case "SEC_ERROR_UNSUPPORTED_KEYALG": - case "SEC_ERROR_UNTRUSTED_CERT": - case "SEC_ERROR_UNTRUSTED_ISSUER": - return true; - default: - return false; - } -} - // The optional argument is only here for testing purposes. function setTechnicalDetailsOnCertError( failedCertInfo = document.getFailedCertSecurityInfo() diff --git a/toolkit/content/aboutNetErrorHelpers.mjs b/toolkit/content/aboutNetErrorHelpers.mjs @@ -164,3 +164,33 @@ export async function recordSecurityUITelemetry(category, name, errorInfo) { } RPMRecordGleanEvent(category, name, extraKeys); } + +// Returns true if the error identified by the given error code string has no +// particular action the user can take to fix it. +export function errorHasNoUserFix(errorCodeString) { + switch (errorCodeString) { + case "MOZILLA_PKIX_ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY": + case "MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING": + case "MOZILLA_PKIX_ERROR_ISSUER_NO_LONGER_TRUSTED": + case "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE": + case "MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH": + case "SEC_ERROR_BAD_DER": + case "SEC_ERROR_BAD_SIGNATURE": + case "SEC_ERROR_CERT_NOT_IN_NAME_SPACE": + case "SEC_ERROR_EXTENSION_VALUE_INVALID": + case "SEC_ERROR_INADEQUATE_CERT_TYPE": + case "SEC_ERROR_INADEQUATE_KEY_USAGE": + case "SEC_ERROR_INVALID_KEY": + case "SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID": + case "SEC_ERROR_REVOKED_CERTIFICATE": + case "SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION": + case "SEC_ERROR_UNSUPPORTED_EC_POINT_FORM": + case "SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE": + case "SEC_ERROR_UNSUPPORTED_KEYALG": + case "SEC_ERROR_UNTRUSTED_CERT": + case "SEC_ERROR_UNTRUSTED_ISSUER": + return true; + default: + return false; + } +} diff --git a/toolkit/content/net-error-card.mjs b/toolkit/content/net-error-card.mjs @@ -16,6 +16,7 @@ import { recordSecurityUITelemetry, gOffline, retryThis, + errorHasNoUserFix, } from "chrome://global/content/aboutNetErrorHelpers.mjs"; import { html } from "chrome://global/content/vendor/lit.all.mjs"; import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; @@ -53,6 +54,7 @@ export class NetErrorCard extends MozLitElement { }; static ERROR_CODES = new Set([ + "SEC_ERROR_REVOKED_CERTIFICATE", "SEC_ERROR_UNKNOWN_ISSUER", "SSL_ERROR_BAD_CERT_DOMAIN", "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT", @@ -119,7 +121,7 @@ export class NetErrorCard extends MozLitElement { "security.certerror.hideAddException", false ); - if (prefValue) { + if (prefValue || errorHasNoUserFix(this.errorInfo.errorCodeString)) { return true; } @@ -176,6 +178,7 @@ export class NetErrorCard extends MozLitElement { introContentTemplate() { switch (this.errorInfo.errorCodeString) { + case "SEC_ERROR_REVOKED_CERTIFICATE": case "SEC_ERROR_UNKNOWN_ISSUER": case "SSL_ERROR_BAD_CERT_DOMAIN": case "SEC_ERROR_EXPIRED_CERTIFICATE": @@ -218,6 +221,19 @@ export class NetErrorCard extends MozLitElement { let content; switch (this.errorInfo.errorCodeString) { + case "SEC_ERROR_REVOKED_CERTIFICATE": { + content = this.advancedSectionTemplate({ + whyDangerousL10nId: "fp-certerror-revoked-why-dangerous-body", + whyDangerousL10nArgs: { + hostname: this.hostname, + }, + whatCanYouDoL10nId: "fp-certerror-revoked-what-can-you-do-body", + learnMoreL10nId: "fp-learn-more-about-cert-issues", + learnMoreSupportPage: "connection-not-secure", + viewCert: true, + }); + break; + } case "SEC_ERROR_UNKNOWN_ISSUER": { content = this.advancedSectionTemplate({ whyDangerousL10nId: "fp-certerror-unknown-issuer-why-dangerous-body", @@ -528,6 +544,11 @@ export class NetErrorCard extends MozLitElement { // error code link in the advanced panel. this.certErrorDebugInfoShowing = false; + if (!this.exceptionButton) { + this.resetReveal = null; + return; + } + // Reveal, but disabled (and grayed-out) for 3.0s. if (this.exceptionButton) { this.exceptionButton.disabled = true; diff --git a/toolkit/locales/en-US/toolkit/neterror/certError.ftl b/toolkit/locales/en-US/toolkit/neterror/certError.ftl @@ -183,6 +183,13 @@ fp-certerror-return-to-previous-page-recommended-button = Go back (Recommended) # This string appears after the following string: "What makes the site look dangerous?" (fp-certerror-why-site-dangerous) # Variables: # $hostname (String) - Hostname of the website to which the user was trying to connect. +fp-certerror-revoked-why-dangerous-body = { -brand-short-name } is warning you about this site because the certificate provided for { $hostname } has been revoked and isn’t trusted anymore. +# This string appears after the following string: "What can you do about it?" (fp-certerror-what-can-you-do) +fp-certerror-revoked-what-can-you-do-body = Probably nothing, since it’s likely there’s a problem with the site itself. You can check with the website owner to see if they are working on the problem. + +# This string appears after the following string: "What makes the site look dangerous?" (fp-certerror-why-site-dangerous) +# Variables: +# $hostname (String) - Hostname of the website to which the user was trying to connect. # $validHosts (String) - Valid hostnames. fp-certerror-bad-domain-why-dangerous-body = The site is set up to allow only secure connections, but there’s a problem with the site’s certificate. It’s possible that a bad actor is trying to impersonate the site. Sites use certificates issued by a certificate authority to prove they’re really who they say they are. { -brand-short-name } doesn’t trust this site because its certificate isn’t valid for { $hostname }. The certificate is only valid for: { $validHosts }. # This string appears after the following string: "What can you do about it?" (fp-certerror-what-can-you-do)