Initial curated CertScope source from fe2bbe3ca5c98bf64be26a09c6c58ee805a7f87e
This commit is contained in:
commit
7348faf6c0
65 files changed
+12509
No files matched your search
@@ -0,0 +1,52 @@
|
||||
import type { InspectionResult } from "./types";
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
|
||||
function finiteScore(value: unknown) {
|
||||
return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 100;
|
||||
}
|
||||
|
||||
function collectorStatus(value: unknown) {
|
||||
return value === "ok" || value === "partial" || value === "failed";
|
||||
}
|
||||
|
||||
function nonNegativeInteger(value: unknown): value is number {
|
||||
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
||||
}
|
||||
|
||||
function isFinding(value: unknown) {
|
||||
if (!isRecord(value)) return false;
|
||||
const category = value.category;
|
||||
const severity = value.severity;
|
||||
return typeof value.id === "string" && typeof value.title === "string" && typeof value.summary === "string"
|
||||
&& (category === "dns" || category === "tls" || category === "http")
|
||||
&& (severity === "critical" || severity === "warning" || severity === "info" || severity === "good")
|
||||
&& typeof value.scoreImpact === "number" && Number.isFinite(value.scoreImpact)
|
||||
&& Array.isArray(value.evidence) && value.evidence.every((pointer) => typeof pointer === "string")
|
||||
&& (value.remediation === undefined || typeof value.remediation === "string");
|
||||
}
|
||||
|
||||
/**
|
||||
* A deliberately small client boundary: it validates the shape ReportView
|
||||
* depends on without duplicating server-side schema logic or adding a runtime
|
||||
* validation dependency to this compact project.
|
||||
*/
|
||||
export function isInspectionResult(value: unknown): value is InspectionResult {
|
||||
if (!isRecord(value) || typeof value.host !== "string" || !value.host || typeof value.inspectedAt !== "string" || !Number.isFinite(Date.parse(value.inspectedAt))) return false;
|
||||
if (!Array.isArray(value.addresses) || !value.addresses.every((address) => typeof address === "string")) return false;
|
||||
const score = value.score;
|
||||
if (!isRecord(score) || !finiteScore(score.overall) || !isRecord(score.categories)) return false;
|
||||
const categories = score.categories;
|
||||
if (!["dns", "tls", "http"].every((category) => finiteScore(categories[category]))) return false;
|
||||
const completeness = value.completeness;
|
||||
if (!isRecord(completeness) || !["complete", "partial", "failed"].includes(String(completeness.status))) return false;
|
||||
const complete = completeness.complete;
|
||||
const total = completeness.total;
|
||||
if (!nonNegativeInteger(complete) || !nonNegativeInteger(total) || complete > total || !isRecord(completeness.collectors) || !Array.isArray(completeness.issues)) return false;
|
||||
const collectors = completeness.collectors;
|
||||
if (!["dns", "tls", "http"].every((category) => collectorStatus(collectors[category])) || !completeness.issues.every((issue) => typeof issue === "string")) return false;
|
||||
if (!Array.isArray(value.findings) || !value.findings.every(isFinding)) return false;
|
||||
return isRecord(value.dns) && isRecord(value.tls) && isRecord(value.http);
|
||||
}
|
||||
Reference in new issue
Block a user