Files

57 lines
2.4 KiB
TypeScript

import type { InspectionResult } from "./types";
function escapeMarkdown(value: unknown) {
return String(value ?? "")
.replace(/\\/g, "\\\\")
.replace(/[|*_\x60[\]<>]/g, "\\$&")
.replace(/\r?\n/g, " ");
}
function jsonFence(value: unknown) {
const json = JSON.stringify(value, null, 2);
const backticks = Math.max(3, Math.max(...(json.match(/\x60+/g) ?? [""]).map((match) => match.length)) + 1);
const fence = "\x60".repeat(backticks);
return [fence + "json", json, fence];
}
export function inspectionToMarkdown(result: InspectionResult): string {
const lines = [
"# CertScope report — " + escapeMarkdown(result.host),
"",
"- Inspected: " + escapeMarkdown(result.inspectedAt),
"- Addresses: " + escapeMarkdown(result.addresses.join(", ") || "none"),
"- Overall health: " + result.score.overall + "/100",
"- DNS: " + result.score.categories.dns + "/100",
"- TLS: " + result.score.categories.tls + "/100",
"- HTTP: " + result.score.categories.http + "/100",
"- Score formula: equal-weighted average of the DNS, TLS and HTTP category scores",
"- Completeness: " + result.completeness.status + " (" + result.completeness.complete + "/" + result.completeness.total + " collectors complete)",
"",
"## Collector status",
"",
];
for (const [category, status] of Object.entries(result.completeness.collectors)) {
lines.push("- " + category.toUpperCase() + ": " + escapeMarkdown(status));
}
if (result.completeness.issues.length) lines.push("- Collector issues: " + escapeMarkdown(result.completeness.issues.join("; ")));
lines.push("", "## Findings", "");
if (!result.findings.length) lines.push("No findings.", "");
for (const finding of result.findings) {
lines.push("### " + escapeMarkdown(finding.title));
lines.push("- Severity: " + escapeMarkdown(finding.severity));
lines.push("- Category: " + escapeMarkdown(finding.category));
lines.push("- Score impact: " + finding.scoreImpact);
lines.push("- Summary: " + escapeMarkdown(finding.summary));
lines.push("- Evidence: " + escapeMarkdown(finding.evidence.join(", ") || "none"));
if (finding.remediation) lines.push("- Remediation: " + escapeMarkdown(finding.remediation));
lines.push("");
}
for (const [title, evidence] of [["DNS", result.dns], ["TLS", result.tls], ["HTTP", result.http]] as const) {
lines.push("## " + title + " evidence", "", ...jsonFence(evidence), "");
}
return lines.join("\n");
}