// Based on https://github.com/mdn/webextensions-examples/blob/main/root-cert-stats/background.js // On header receive, inspect cert and update app icon as required async function onHeaderReceive(details) { if (details.tabId < 0) // tabId < 0 means non-user tab return; try { await certInspectUpdate(details.requestId, details.tabId); } catch(error) { console.error(error); } } function daysBetween(a, b) { return Math.ceil((b - a) / (24 * 60 * 60 * 1000)); } async function certInspectUpdate(requestId, tabId) { let securityInfo = await browser.webRequest.getSecurityInfo(requestId, {}); // Not HTTPS if (securityInfo.state !== "secure") return; // Grab cert itself let cert = securityInfo.certificates[0]; // Check expiry against 30 days let in30d = new Date(); in30d.setDate(in30d.getDate() + 30); let expiry = new Date(cert.validity.end); // HACK: Schedule for this to run after a little later for 'blocking' element // This is so that main_frame doesn't clear tabId-specific things again setTimeout(() => { let bad = expiry <= in30d; setIcon(bad ? "nope" : "ok", tabId); let days = daysBetween(new Date(), expiry); let daysShort = days + "d"; if (days > 90) daysShort = "3m+"; else if (days > 180) daysShort = "6m+"; else if (days > 365) daysShort = "1y+"; // Set expiry days as text browser.browserAction.setBadgeText({ text: daysShort, tabId: tabId }); browser.browserAction.setTitle({ title: "certose - " + cert.issuer + " - " + days + "d to validity end", tabId: tabId }); if (!bad) browser.browserAction.setBadgeBackgroundColor({ color: "blue" }); }, 100); } function setIcon(icon, tabId) { browser.browserAction.setIcon({ path: "icons/" + icon + ".png", tabId: tabId }); } // Listen for all header receive events, which contain the cert details we want // Firefox only supports onHeadersReceived at the moment, hence the hack above browser.webRequest.onHeadersReceived.addListener( onHeaderReceive, { urls: [""], types: ["main_frame"] // Only for the top level website }, [ "blocking" ] );