57 lines
2.4 KiB
TypeScript
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");
|
|
}
|