diff --git a/public/app.js b/public/app.js index 3a4e769..7d63562 100644 --- a/public/app.js +++ b/public/app.js @@ -199,3 +199,167 @@ dnsForm.addEventListener("submit", async (event) => { dnsButton.textContent = "Look Up DNS"; } }); +// --------------------------------------------------------- +// SSL Certificate Checker +// --------------------------------------------------------- + +// Get references to the SSL form and result elements. +const sslForm = document.getElementById("ssl-form"); +const sslHostnameInput = document.getElementById( + "ssl-hostname-input" +); +const sslButton = document.getElementById("ssl-button"); +const sslMessage = document.getElementById("ssl-message"); +const sslResultPanel = document.getElementById( + "ssl-result-panel" +); + +const sslStatusBadge = document.getElementById( + "ssl-status-badge" +); +const sslHostname = document.getElementById("ssl-hostname"); +const sslDaysRemaining = document.getElementById( + "ssl-days-remaining" +); +const sslValidFrom = document.getElementById("ssl-valid-from"); +const sslValidTo = document.getElementById("ssl-valid-to"); +const sslProtocol = document.getElementById("ssl-protocol"); +const sslCipher = document.getElementById("ssl-cipher"); +const sslAddress = document.getElementById("ssl-address"); +const sslCheckedAt = document.getElementById("ssl-checked-at"); + +const sslSubject = document.getElementById("ssl-subject"); +const sslIssuer = document.getElementById("ssl-issuer"); +const sslFingerprint = document.getElementById( + "ssl-fingerprint" +); + +const sslAuthorizationErrorBlock = document.getElementById( + "ssl-authorization-error-block" +); +const sslAuthorizationError = document.getElementById( + "ssl-authorization-error" +); + +// Convert certificate date strings into a readable local date. +function formatCertificateDate(value) { + if (!value) { + return "Unknown"; + } + + const date = new Date(value); + + if (Number.isNaN(date.getTime())) { + return value; + } + + return date.toLocaleString(); +} + +// Choose readable text for the certificate expiration state. +function getExpirationText(daysRemaining) { + if (daysRemaining === null || daysRemaining === undefined) { + return "Unknown"; + } + + if (daysRemaining < 0) { + return `${Math.abs(daysRemaining)} days expired`; + } + + if (daysRemaining === 0) { + return "Expires today"; + } + + return `${daysRemaining} days`; +} + +// Handle SSL certificate form submissions. +sslForm.addEventListener("submit", async (event) => { + event.preventDefault(); + + const hostname = sslHostnameInput.value.trim(); + + // Reset the SSL interface before beginning the request. + sslMessage.textContent = "Checking certificate..."; + sslMessage.className = "status-message"; + sslResultPanel.hidden = true; + sslButton.disabled = true; + sslButton.textContent = "Checking..."; + + try { + const response = await fetch("/api/ssl", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ hostname }), + }); + + const result = await response.json(); + + if (!response.ok) { + throw new Error( + result.error || "The SSL certificate check failed." + ); + } + + // Show whether Node trusted the certificate chain and hostname. + if (result.authorized) { + sslStatusBadge.textContent = "Valid"; + sslStatusBadge.className = "status-badge online"; + } else { + sslStatusBadge.textContent = "Not trusted"; + sslStatusBadge.className = "status-badge offline"; + } + + // Populate the certificate summary. + sslHostname.textContent = result.hostname; + sslDaysRemaining.textContent = getExpirationText( + result.daysRemaining + ); + + sslValidFrom.textContent = formatCertificateDate( + result.validFrom + ); + sslValidTo.textContent = formatCertificateDate( + result.validTo + ); + + sslProtocol.textContent = result.protocol || "Unknown"; + sslCipher.textContent = result.cipher || "Unknown"; + + sslAddress.textContent = result.resolvedAddress + ? `${result.resolvedAddress} (IPv${result.addressFamily})` + : "Unknown"; + + sslCheckedAt.textContent = formatCertificateDate( + result.checkedAt + ); + + // Populate the longer certificate values. + sslSubject.textContent = result.subject || "Unknown"; + sslIssuer.textContent = result.issuer || "Unknown"; + sslFingerprint.textContent = + result.fingerprint256 || "Unavailable"; + + // Show the authorization problem only when one exists. + if (result.authorizationError) { + sslAuthorizationError.textContent = + result.authorizationError; + sslAuthorizationErrorBlock.hidden = false; + } else { + sslAuthorizationError.textContent = ""; + sslAuthorizationErrorBlock.hidden = true; + } + + sslMessage.textContent = + "Certificate check completed."; + sslResultPanel.hidden = false; + } catch (error) { + sslMessage.textContent = error.message; + sslMessage.className = "status-message error"; + } finally { + sslButton.disabled = false; + sslButton.textContent = "Check Certificate"; + } +}); \ No newline at end of file diff --git a/public/index.html b/public/index.html index 8f68fa3..d07f079 100644 --- a/public/index.html +++ b/public/index.html @@ -17,7 +17,8 @@
BYTEGEIST TOOLBOX
- Check a website's current status or inspect its public DNS records. + Check a website's current status, inspect its public DNS records, + or review its SSL certificate.
@@ -39,6 +40,15 @@ > DNS Lookup + + @@ -153,9 +163,123 @@ + + ++ Inspect the public TLS certificate for a website and view its issuer, + expiration date, authorization status, protocol, cipher, and fingerprint. +
+-
+-
+-
+