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"); }