Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion server/src/controllers/controllerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ type SSLCheckerType = typeof sslChecker;
export const fetchMonitorCertificate = async (checker: SSLCheckerType, monitor: Monitor): Promise<SSLDetails> => {
const monitorUrl = new URL(monitor.url);
const hostname = monitorUrl.hostname;
const cert = await checker(hostname);
const urlPort = monitorUrl.port ? Number(monitorUrl.port) : undefined;
const port = monitor.port ?? urlPort;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should probably be inverted, an explicit port set in the URL should probably take precedence over the monitor.port field.

This is a design decision, but I think the visually prominent port in the monitor URL should take precedence over a field that is not readily visible in the monitor config 🤔

Consider the case where a user has set their url to be https://www.myurl.com:52345/health and accidentally set port 443 on the monitor config. We will silently be checking on 443 while the user would be left wondering why port 52345 as declared in their URL is not responding.

Again a design decision, but I think probably the right call here.

const cert = await checker(hostname, port ? { port } : undefined);
if (cert?.validTo === null || cert?.validTo === undefined) {
throw new Error("Certificate not found");
}
Expand Down
55 changes: 55 additions & 0 deletions server/test/unit/controllers/controllerUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { describe, expect, it, jest } from "@jest/globals";
import { fetchMonitorCertificate } from "../../../src/controllers/controllerUtils.ts";
import type { Monitor } from "../../../src/types/index.ts";
import type { SSLDetails } from "ssl-checker";

const certificate: SSLDetails = {
daysRemaining: 30,
valid: true,
validFrom: "2026-01-01T00:00:00.000Z",
validTo: "2026-06-01T00:00:00.000Z",
validFor: ["checkmate.example.org"],
};

const makeMonitor = (overrides?: Partial<Monitor>): Monitor =>
({
id: "monitor-1",
teamId: "team-1",
url: "https://checkmate.example.org",
ignoreTlsErrors: false,
...overrides,
}) as Monitor;

describe("controllerUtils", () => {
describe("fetchMonitorCertificate", () => {
it("checks the explicit HTTPS URL port when present", async () => {
const checker = jest.fn().mockResolvedValue(certificate);

await fetchMonitorCertificate(checker as any, makeMonitor({ url: "https://checkmate.example.org:54321/status" }));

expect(checker).toHaveBeenCalledWith("checkmate.example.org", { port: 54321 });
});

it("uses the monitor port when the URL does not include one", async () => {
const checker = jest.fn().mockResolvedValue(certificate);

await fetchMonitorCertificate(checker as any, makeMonitor({ url: "https://checkmate.example.org/status", port: 8443 }));

expect(checker).toHaveBeenCalledWith("checkmate.example.org", { port: 8443 });
});

it("keeps the previous default-port behavior when no port is configured", async () => {
const checker = jest.fn().mockResolvedValue(certificate);

await fetchMonitorCertificate(checker as any, makeMonitor());

expect(checker).toHaveBeenCalledWith("checkmate.example.org", undefined);
});

it("throws when no certificate expiry is returned", async () => {
const checker = jest.fn().mockResolvedValue({ ...certificate, validTo: undefined });

await expect(fetchMonitorCertificate(checker as any, makeMonitor())).rejects.toThrow("Certificate not found");
});
});
});
Loading