Bug 1568974 - Adds error warning to certificate viewer. r=johannh,fluent-reviewers,flod

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
dleblanccyr 2019-08-13 14:09:44 +00:00
Родитель 93ddb7aece
Коммит fe443d7006
14 изменённых файлов: 244 добавлений и 14 удалений

Просмотреть файл

@ -3645,6 +3645,10 @@ var BrowserOnClick = {
let certsStringURL = certs.map(elem => `cert=${elem}`);
certsStringURL = certsStringURL.join("&");
let url = `about:certificate?${certsStringURL}`;
let error = securityInfo.errorCodeString;
if (error) {
url = `${url}&error=${error}`;
}
openTrustedLinkIn(url, "tab", {
triggeringPrincipal: browser.contentPrincipal,
});

Просмотреть файл

@ -35,7 +35,7 @@ var security = {
viewCertHelper(window, cert);
},
_getSecurityInfo() {
async _getSecurityInfo() {
// We don't have separate info for a frame, return null until further notice
// (see bug 138479)
if (!this.windowInfo.isTopWindow) {
@ -56,9 +56,16 @@ var security = {
Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT);
var isInsecure = ui.state & Ci.nsIWebProgressListener.STATE_IS_INSECURE;
var isEV = ui.state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
var secInfo = ui.secInfo;
if (!isInsecure && secInfo) {
let secInfo = await window.opener.gBrowser.selectedBrowser.browsingContext.currentWindowGlobal.getSecurityInfo();
secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
let error = null;
if (secInfo.errorCodeString) {
error = secInfo.errorCodeString;
}
if (secInfo) {
var cert = secInfo.serverCert;
var issuerName = cert.issuerOrganization || cert.issuerName;
@ -73,6 +80,7 @@ var security = {
isEV,
cert,
certificateTransparency: undefined,
error,
};
var version;
@ -132,6 +140,7 @@ var security = {
isEV,
cert: null,
certificateTransparency: null,
error,
};
},
@ -204,9 +213,9 @@ var security = {
/**
* Open the login manager window
*/
viewPasswords() {
async viewPasswords() {
LoginHelper.openPasswordManager(window, {
filterString: this._getSecurityInfo().hostName,
filterString: await this._getSecurityInfo().hostName,
entryPoint: "pageinfo",
});
},
@ -214,10 +223,11 @@ var security = {
_cert: null,
};
function securityOnLoad(uri, windowInfo) {
async function securityOnLoad(uri, windowInfo) {
security.init(uri, windowInfo);
var info = security._getSecurityInfo();
var info = await security._getSecurityInfo();
if (
!info ||
(uri.scheme === "about" && !uri.spec.startsWith("about:certerror"))
@ -276,6 +286,9 @@ function securityOnLoad(uri, windowInfo) {
/* Manage the View Cert button*/
var viewCert = document.getElementById("security-view-cert");
if (info.cert) {
if (info.error) {
security.error = info.error;
}
security._cert = info.cert;
viewCert.collapsed = false;
} else {
@ -384,21 +397,28 @@ function getCertificateChain(certChain, options = {}) {
return certificates;
}
function viewCertHelper(parent, cert) {
async function viewCertHelper(parent, cert) {
if (!cert) {
return;
}
if (Services.prefs.getBoolPref("security.aboutcertificate.enabled")) {
let ui = security._getSecurityUI();
let securityInfo = ui.secInfo;
let certChain = getCertificateChain(securityInfo.succeededCertChain);
let securityInfo = await window.opener.gBrowser.selectedBrowser.browsingContext.currentWindowGlobal.getSecurityInfo();
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
let certChain = securityInfo.succeededCertChain
? securityInfo.succeededCertChain
: securityInfo.failedCertChain;
certChain = getCertificateChain(certChain);
let certs = certChain.map(elem =>
encodeURIComponent(elem.getBase64DERString())
);
let certsStringURL = certs.map(elem => `cert=${elem}`);
certsStringURL = certsStringURL.join("&");
let url = `about:certificate?${certsStringURL}`;
if (security.error) {
url = url + `&error=${security.error}`;
}
openTrustedLinkIn(url, "tab", {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
});

Просмотреть файл

@ -59,4 +59,16 @@ certificate-viewer-signature-scheme = Signature Scheme
certificate-viewer-timestamp = Timestamp
certificate-viewer-value = Value
certificate-viewer-version = Version
## Error codes
certificate-viewer-warning-section-title = Warning
certificate-viewer-sec-error-expired-certificate = This certificate has expired.
certificate-viewer-ssl-error-bad-cert-domain = This certificate is not valid for this host.
certificate-viewer-mozilla-pkix-error-self-signed-cert = This certificate is not trusted because it is self-signed.
certificate-viewer-mozilla-pkix-error-key-pinning-failure = No trusted certificate chain could be constructed that matches the pinset.
certificate-viewer-mozilla-pkix-error-mitm-detected = MITM software has been detected.
certificate-viewer-sec-error-unknown-issuer = This certificates issuer is unknown.
certificate-viewer-sec-error-revoked-certificate = Peers certificate has been revoked.
certificate-viewer-sec-error-cert-signature-algorithm-disabled = The certificate is not trusted because it was signed using a signature algorithm that was disabled because that algorithm is not secure.
certificate-viewer-business-category = Business Category

Просмотреть файл

@ -21,8 +21,9 @@
<script defer="defer" type="module" src="chrome://global/content/certviewer/certviewer.js"></script>
<script defer="defer" type="module" src="chrome://global/content/certviewer/components/info-group.js"></script>
<script defer="defer" type="module" src="chrome://global/content/certviewer/components/info-item.js"></script>
<script defer="defer" type="module" src="chrome://global/content/certviewer/components/certificate-section.js"></script>
<script defer="defer" type="module" src="chrome://global/content/certviewer/components/warning-section.js"></script>
<script defer="defer" type="module" src="chrome://global/content/certviewer/components/error-section.js"></script>
<script defer="defer" type="module" src="chrome://global/content/certviewer/components/certificate-section.js"></script>
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
<link rel="stylesheet" href="chrome://global/content/certviewer/certviewer.css">
<title>about:certificate</title>
@ -52,5 +53,13 @@
<h1 class="title"></h1>
<span class="error"></span>
</template>
<template id="warning-section-template">
<link rel="stylesheet" href="chrome://global/content/certviewer/components/warning-section.css">
<div class="warning-container">
<h2 class="warning-title" data-l10n-id="certificate-viewer-warning-section-title"></h1>
<span class="warning-message"></span>
</div>
</template>
</body>
</html>

Просмотреть файл

@ -9,6 +9,8 @@
import { parse } from "./certDecoder.js";
import { pemToDER } from "./utils.js";
let errorCode;
document.addEventListener("DOMContentLoaded", async e => {
let url = new URL(document.URL);
let certInfo = url.searchParams.getAll("cert");
@ -17,9 +19,71 @@ document.addEventListener("DOMContentLoaded", async e => {
return;
}
certInfo = certInfo.map(cert => decodeURIComponent(cert));
errorCode = url.searchParams.get("error");
await buildChain(certInfo);
});
async function highlightErrorInfoItem(error) {
let infoItems = [];
switch (error) {
case "SEC_ERROR_EXPIRED_CERTIFICATE":
infoItems.push({
group: "validity",
infoItem: "not-after",
});
break;
case "SSL_ERROR_BAD_CERT_DOMAIN":
infoItems.push({
group: "subject-name",
infoItem: "common-name",
});
infoItems.push({
group: "subject-alt-names",
});
break;
case "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT":
infoItems.push({
group: "issuer-name",
});
break;
case "SEC_ERROR_UNKNOWN_ISSUER":
infoItems.push({
group: "issuer-name",
});
break;
case "SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED":
infoItems.push({
group: "embedded-scts",
infoItem: "signature-algorithm",
});
break;
case "MOZILLA_PKIX_ERROR_MITM_DETECTED":
infoItems.push({
group: "issuer-name",
});
break;
}
if (infoItems.length) {
customElements.whenDefined("info-group").then(() => {
let certSection = document.querySelector("certificate-section");
for (let i = 0; i < infoItems.length; i++) {
let item = infoItems[i];
let groupItem = certSection.shadowRoot.getElementById(item.group);
if (item.infoItem) {
let infoItem = groupItem.shadowRoot.querySelector(
"." + item.infoItem
);
infoItem.classList.add("warning");
} else {
groupItem.classList.add("warning");
}
}
});
}
}
export const updateSelectedItem = (() => {
let state;
return selectedItem => {
@ -284,7 +348,14 @@ const adjustCertInformation = cert => {
const render = async (certs, error) => {
await customElements.whenDefined("certificate-section");
const CertificateSection = customElements.get("certificate-section");
document.querySelector("body").append(new CertificateSection(certs, error));
if (errorCode) {
document
.querySelector("body")
.append(new CertificateSection(certs, error, errorCode));
highlightErrorInfoItem(errorCode);
} else {
document.querySelector("body").append(new CertificateSection(certs, error));
}
return Promise.resolve();
};

Просмотреть файл

@ -21,6 +21,11 @@ h1 {
display: contents;
}
.error {
padding-bottom: 10px;
border-bottom: 1px solid var(--in-content-box-border-color);
}
.tab {
margin: 0;
border-radius: 0;

Просмотреть файл

@ -5,12 +5,14 @@
import { updateSelectedItem } from "../certviewer.js";
import { InfoGroup } from "./info-group.js";
import { ErrorSection } from "./error-section.js";
import { WarningSection } from "./warning-section.js";
class CertificateSection extends HTMLElement {
constructor(certs, error) {
constructor(certs, error, errorCode) {
super();
this.certs = certs;
this.error = error;
this.errorCode = errorCode;
}
connectedCallback() {
@ -35,6 +37,10 @@ class CertificateSection extends HTMLElement {
"certificate-viewer-certificate-section-title"
);
if (this.errorCode) {
this.shadowRoot.prepend(new WarningSection(this.errorCode));
}
this.infoGroupContainer = this.shadowRoot.querySelector(".info-groups");
if (this.error) {

Просмотреть файл

@ -32,3 +32,7 @@
right: 0;
bottom: 0;
}
:host(.warning) {
border: 2px solid var(--yellow-50);
}

Просмотреть файл

@ -22,6 +22,13 @@ export class InfoGroup extends HTMLElement {
let title = this.shadowRoot.querySelector(".info-group-title");
title.textContent = this.item.sectionTitle;
// Adds an id with the section title's name, to make
// it easier to find when highlighting errors.
this.setAttribute(
"id",
this.item.sectionTitle.replace(/\s+/g, "-").toLowerCase()
);
for (let i = 0; i < this.item.sectionItems.length; i++) {
this.shadowRoot.append(new InfoItem(this.item.sectionItems[i]));
}

Просмотреть файл

@ -13,6 +13,7 @@ label {
color: var(--in-content-text-color);
font-weight: 700;
font-size: 1em;
position: relative;
}
label:dir(rtl) {
@ -26,3 +27,25 @@ label:dir(rtl) {
text-overflow: ellipsis;
white-space: nowrap;
}
:host(.warning) * {
border: 2px solid var(--yellow-50);
}
:host(.warning) label {
border-right: 0;
}
:host(.warning) span {
border-left: 0;
}
:host(.warning) label::after {
content: "";
display: inline;
position: absolute;
height: 100%;
width: 15px;
border-top: 2px solid var(--yellow-50);
border-bottom: 2px solid var(--yellow-50);
top: -2px;
}

Просмотреть файл

@ -31,6 +31,8 @@ export class InfoItem extends HTMLElement {
.toLowerCase();
label.setAttribute("data-l10n-id", "certificate-viewer-" + labelText);
this.classList.add(labelText);
let info = this.shadowRoot.querySelector(".info");
info.textContent = this.item.info;

Просмотреть файл

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
:host {
display: block;
}
.warning-container {
background-image: url("chrome://browser/skin/cert-error.svg");
background-position: left center;
background-repeat: no-repeat;
background-size: 30% 80%;
display: grid;
max-width: 500px;
margin: 0 auto;
padding: 20px 0;
grid-template-columns: 30% 70%;
}
.warning-title {
margin: 5px 0;
grid-column-start: 2;
}
.warning-message {
display: block;
grid-column-start: 2;
}

Просмотреть файл

@ -0,0 +1,36 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export class WarningSection extends HTMLElement {
constructor(errorCode) {
super();
this.errorCode = errorCode;
}
connectedCallback() {
let template = document.getElementById("warning-section-template");
let templateHtml = template.content.cloneNode(true);
this.attachShadow({ mode: "open" });
document.l10n.connectRoot(this.shadowRoot);
this.shadowRoot.appendChild(templateHtml);
this.render();
}
render() {
let warningMessage = this.errorCode.replace(/\s+/g, "-").toLowerCase();
let messageElement = this.shadowRoot.querySelector(".warning-message");
warningMessage = warningMessage.replace(/_/g, "-");
messageElement.setAttribute(
"data-l10n-id",
"certificate-viewer-" + warningMessage
);
}
}
customElements.define("warning-section", WarningSection);

Просмотреть файл

@ -21,3 +21,5 @@ toolkit.jar:
content/global/certviewer/pvutils_bundle.js (content/vendor/pvutils_bundle.js)
content/global/certviewer/asn1js_bundle.js (content/vendor/asn1js_bundle.js)
content/global/certviewer/pkijs_bundle.js (content/vendor/pkijs_bundle.js)
content/global/certviewer/components/warning-section.css (content/components/warning-section.css)
content/global/certviewer/components/warning-section.js (content/components/warning-section.js)