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:
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)