Expand TrustLab certificate failure scenarios

This commit is contained in:
sergeych committed 2026-08-16 16:27:20 +04:00
1 parent 9830f444ac
commit f1fcef9b85
7 files changed
+240 -16

No files matched your search

+44 -3
View File
@@ -1,5 +1,13 @@
import { unknownLocalAuthority, validPublicCertificate } from "../fixtures/tls.js";
import {
conflictingCommunityAdvice,
explicitlyDistrustedAuthority,
expiredLeafCertificate,
hostnameMismatch,
unknownLocalAuthority,
validPublicCertificate,
} from "../fixtures/tls.js";
import {
createCommunityAdvicePlugin,
createFirefoxValidationPlugin,
createUserDecisionPlugin,
createVillageCommunityPlugin,
@@ -16,6 +24,36 @@ const scenarios = {
label: "Valid conventional path",
facts: validPublicCertificate,
},
"expired-leaf": {
label: "Expired server certificate",
facts: expiredLeafCertificate,
},
"hostname-mismatch": {
label: "Hostname mismatch",
facts: hostnameMismatch,
},
"distrusted-authority": {
label: "Explicitly distrusted authority",
facts: explicitlyDistrustedAuthority,
},
"conflicting-advice": {
label: "Conflicting community advice",
facts: conflictingCommunityAdvice,
plugins: [
createCommunityAdvicePlugin({
id: "community.archivists",
name: "Regional archivists",
trusted: true,
message: "The archivists recognize this exact certificate and recommend trust.",
}),
createCommunityAdvicePlugin({
id: "community.network-watch",
name: "Independent network watch",
trusted: false,
message: "The network observers report an unexpected certificate change.",
}),
],
},
};
const state = {
@@ -68,7 +106,11 @@ elements.clear.addEventListener("click", () => {
async function render() {
const facts = scenarios[state.scenario].facts;
const plugins = [createFirefoxValidationPlugin()];
if (state.communityEnabled) plugins.push(createVillageCommunityPlugin());
if (state.communityEnabled) {
plugins.push(
...(scenarios[state.scenario].plugins ?? [createVillageCommunityPlugin()]),
);
}
const userPlugin = createUserDecisionPlugin(state.userDecision);
if (userPlugin) plugins.push(userPlugin);
@@ -156,4 +198,3 @@ function formatCode(value = "") {
}
render();
+22 -9
View File
@@ -29,7 +29,9 @@ export function verdictCopy(result, hasUserDecision) {
return {
eyebrow: "Decision required",
title: "Firefox could not verify this identity",
detail: "Review the broken path and attributed plugin findings before deciding.",
detail:
result.facts.failure?.summary ??
"Review the broken path and attributed plugin findings before deciding.",
};
}
@@ -37,19 +39,30 @@ export function chainRows(facts) {
const chain = facts.constructedChain.length
? facts.constructedChain
: facts.presentedChain;
const failureAtEnd = facts.validation === "failure";
const failedFingerprint = facts.failure?.certificateSha256;
return chain.map((certificate, index) => ({
...certificate,
name: subjectName(certificate.subject),
role:
index === 0 ? "Leaf certificate" : index === chain.length - 1 ? "Root candidate" : "Intermediate CA",
edge:
index === chain.length - 1
? failureAtEnd
? "Not anchored in current Firefox trust"
: "Trusted by current Firefox policy"
: "Signature links to next issuer",
failed: index === chain.length - 1 && failureAtEnd,
edge: edgeDescription(facts, certificate, index, chain.length),
failed: certificate.sha256 === failedFingerprint,
}));
}
function edgeDescription(facts, certificate, index, chainLength) {
if (certificate.sha256 === facts.failure?.certificateSha256) {
const messages = {
"unknown-issuer": "Not anchored in current Firefox trust",
expired: "Certificate validity period has ended",
"hostname-mismatch": `Does not identify ${facts.hostname}`,
"explicitly-distrusted-authority": "Explicitly distrusted by local Browsec policy",
};
return messages[facts.failure.code] ?? facts.failure.summary;
}
return index === chainLength - 1
? "Trusted by current Firefox policy"
: "Signature links to next issuer";
}
+1 -1
View File
@@ -229,6 +229,7 @@ select {
}
.journal-entry.kind-warning { border-color: var(--amber); }
.journal-entry.kind-vote { border-color: #9b8bea; }
.journal-entry.kind-resolution { border-color: var(--cyan); }
.journal-entry h3, .journal-entry p { margin: 0; }
.journal-entry h3 { font-size: 0.86rem; }
@@ -283,4 +284,3 @@ select {
.decision-bar div { flex-basis: 100%; }
.button { flex: 1; }
}