tor-browser

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

commit 790042ee65f5ad8a55de040e3d96c80dc4f70a28
parent f17a6b4acb88a6fdb32f73bd664ee6b65bc0b5a5
Author: Jack Brown <jbrown@mozilla.com>
Date:   Tue,  2 Dec 2025 19:04:31 +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 | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/content/net-error-card.mjs | 21+++++++++++++++++++++
Mtoolkit/locales/en-US/toolkit/neterror/certError.ftl | 7+++++++
4 files changed, 132 insertions(+), 0 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,102 @@ +/* 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.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/net-error-card.mjs b/toolkit/content/net-error-card.mjs @@ -53,6 +53,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", @@ -176,6 +177,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 +220,20 @@ 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, + proceedButton: false, + }); + 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)