fix(monitors): bound certificate probe and gate client on scheme

Addresses review feedback on #3848.

Timeout: the probe now dials a user-defined port, so it can reach a
service that accepts the connection but never completes the TLS
handshake, hanging the request indefinitely. ssl-checker already wires
a "timeout" listener that rejects with "Timed Out", but its defaults
set no timeout, so it never fired. Passing one activates it.

The bundled type declaration is a hand-maintained subset of
https.RequestOptions and omitted "timeout"; it is added there so the
call typechecks against an option the library already supports.

Client gating: "http" is a single monitor type covering both schemes,
so an http:// monitor passed the existing type check and requested a
certificate anyway. With the server now rejecting those, the details
page would surface a 400 on every visit. The scheme is gated client
side to match, and the stat box falls back to "N/A" as before.

Test comment removed as requested; the timeout is covered by a new
assertion in the same file.
This commit is contained in:
gorkem-bwl committed 2026-08-25 18:14:25 -04:00
1 parent 25a7ac4877
commit a672f7604d
4 files changed
+31 -8

No files matched your search

@@ -8,6 +8,9 @@ type SSLCheckerType = typeof sslChecker;
type WhoisModule = typeof whoiser;
const DEFAULT_HTTPS_PORT = 443;
// Ports are user-defined, so a probe can dial something that accepts the
// connection and never completes the handshake. Bound the wait explicitly.
const CERTIFICATE_TIMEOUT_MS = 10_000;
export const fetchMonitorCertificate = async (checker: SSLCheckerType, monitor: Monitor): Promise<SSLDetails> => {
const monitorUrl = new URL(monitor.url);
@@ -20,7 +23,7 @@ export const fetchMonitorCertificate = async (checker: SSLCheckerType, monitor:
// URL.port is "" for the scheme's default port, so fall back to 443; without
// this the certificate of a monitor on a non-standard port was probed on 443.
const port = monitorUrl.port === "" ? DEFAULT_HTTPS_PORT : Number(monitorUrl.port);
const cert = await checker(hostname, { port });
const cert = await checker(hostname, { port, timeout: CERTIFICATE_TIMEOUT_MS });
if (cert?.validTo === null || cert?.validTo === undefined) {
throw new Error("Certificate not found");
}