Files
CertScope-Public/lib/inspect/tls.test.ts
T

119 lines
5.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { EventEmitter } from "node:events";
import tls from "node:tls";
import { certificateDaysRemaining } from "./tls";
import { collectTls, type TlsConnector } from "./tls";
describe("TLS certificate date handling", () => {
it("returns whole days remaining from a supplied clock", () => {
const now = Date.parse("2026-01-01T12:00:00.000Z");
expect(certificateDaysRemaining("Jan 11 12:00:00 2026 GMT", now)).toBe(10);
expect(certificateDaysRemaining("Dec 31 12:00:00 2025 GMT", now)).toBe(-1);
});
it("does not turn an invalid certificate date into a score deduction", () => {
expect(certificateDaysRemaining("not-a-certificate-date", Date.now())).toBeNull();
expect(certificateDaysRemaining(null, Date.now())).toBeNull();
});
it("tries the next validated address after a failed TLS connection", async () => {
const attempted: string[] = [];
const connect: TlsConnector = ((options) => {
const address = String(options.host);
attempted.push(address);
const socket = new EventEmitter() as EventEmitter & {
authorized: boolean;
authorizationError: null;
getPeerCertificate: () => Record<string, unknown>;
getProtocol: () => string;
destroy: () => void;
};
socket.destroy = () => undefined;
if (address === "1.1.1.1") {
queueMicrotask(() => socket.emit("error", Object.assign(new Error("refused"), { code: "ECONNREFUSED" })));
} else {
socket.authorized = true;
socket.authorizationError = null;
socket.getProtocol = () => "TLSv1.3";
socket.getPeerCertificate = () => ({
subject: { CN: "example.com" },
issuer: { CN: "Example CA" },
subjectaltname: "DNS:example.com",
valid_from: "Jan 01 00:00:00 2026 GMT",
valid_to: "Jan 01 00:00:00 2027 GMT",
fingerprint256: "AA:BB",
});
queueMicrotask(() => socket.emit("secureConnect"));
}
return socket as unknown as ReturnType<typeof tls.connect>;
}) as TlsConnector;
const result = await collectTls("example.com", ["1.1.1.1", "2.2.2.2"], { connect, now: Date.parse("2026-06-01T00:00:00Z") });
expect(result).toMatchObject({ address: "2.2.2.2", protocol: "TLSv1.3", hostnameValid: true, authorized: true });
expect(attempted).toEqual(["1.1.1.1", "2.2.2.2"]);
expect(result.attempts).toEqual([{ address: "1.1.1.1", error: "TLS probe failed (ECONNREFUSED)." }]);
});
it("reports certificates that are not yet valid", async () => {
const connect: TlsConnector = (() => {
const socket = new EventEmitter() as EventEmitter & {
authorized: boolean;
authorizationError: null;
getPeerCertificate: () => Record<string, unknown>;
getProtocol: () => string;
destroy: () => void;
};
socket.authorized = true;
socket.authorizationError = null;
socket.getProtocol = () => "TLSv1.3";
socket.getPeerCertificate = () => ({
subjectaltname: "DNS:example.com",
valid_from: "Jul 01 00:00:00 2026 GMT",
valid_to: "Jan 01 00:00:00 2027 GMT",
});
socket.destroy = () => undefined;
queueMicrotask(() => socket.emit("secureConnect"));
return socket as unknown as ReturnType<typeof tls.connect>;
}) as TlsConnector;
const result = await collectTls("example.com", ["2.2.2.2"], { connect, now: Date.parse("2026-06-01T00:00:00Z") });
expect(result.notYetValid).toBe(true);
expect(result.daysRemaining).toBe(214);
});
it("marks malformed or inverted certificate validity dates", async () => {
const connect: TlsConnector = (() => {
const socket = new EventEmitter() as EventEmitter & {
authorized: boolean;
authorizationError: null;
getPeerCertificate: () => Record<string, unknown>;
getProtocol: () => string;
destroy: () => void;
};
socket.authorized = true;
socket.authorizationError = null;
socket.getProtocol = () => "TLSv1.3";
socket.getPeerCertificate = () => ({
subjectaltname: "DNS:example.com",
valid_from: "Jan 02 00:00:00 2026 GMT",
valid_to: "Jan 01 00:00:00 2026 GMT",
});
socket.destroy = () => undefined;
queueMicrotask(() => socket.emit("secureConnect"));
return socket as unknown as ReturnType<typeof tls.connect>;
}) as TlsConnector;
const result = await collectTls("example.com", ["2.2.2.2"], { connect, now: Date.parse("2026-01-01T00:00:00Z") });
expect(result).toMatchObject({ validityDatesValid: false, daysRemaining: 0 });
});
it("marks an exhausted address set as a failed collector", async () => {
const connect: TlsConnector = (() => {
const socket = new EventEmitter() as EventEmitter & { destroy: () => void };
socket.destroy = () => undefined;
queueMicrotask(() => socket.emit("error", Object.assign(new Error("unreachable"), { code: "ETIMEDOUT" })));
return socket as unknown as ReturnType<typeof tls.connect>;
}) as TlsConnector;
const result = await collectTls("example.com", ["2.2.2.2"], { connect, deadline: Date.now() + 1_000 });
expect(result._meta).toMatchObject({ status: "failed" });
});
});