securl 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +19 -0
- package/dist/evidenceQuality.d.ts +2 -0
- package/dist/evidenceQuality.js +154 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +23 -9
- package/dist/postureDigest.d.ts +29 -1
- package/dist/postureDigest.js +13 -0
- package/dist/types.d.ts +38 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@ The format is based on Keep a Changelog and this package follows Semantic Versio
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.13.0] - 2026-06-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Added `buildEvidenceQualitySummary()` and the `securl/evidence-quality` package export for scan confidence, coverage gaps, and recommended follow-up.
|
|
13
|
+
- Added `evidenceQuality` to completed analysis results and compact posture digests so API, mobile, and CLI clients can explain how trustworthy a scan read is.
|
|
14
|
+
|
|
9
15
|
## [1.12.0] - 2026-06-27
|
|
10
16
|
|
|
11
17
|
### Added
|
package/README.md
CHANGED
|
@@ -287,6 +287,23 @@ console.log({
|
|
|
287
287
|
});
|
|
288
288
|
```
|
|
289
289
|
|
|
290
|
+
Version `1.13.0+` includes an evidence-quality helper for client surfaces that need to explain how much confidence to place in a scan result.
|
|
291
|
+
|
|
292
|
+
```js
|
|
293
|
+
import { buildEvidenceQualitySummary } from "securl/evidence-quality";
|
|
294
|
+
|
|
295
|
+
const quality = buildEvidenceQualitySummary(resultWithEvidence);
|
|
296
|
+
|
|
297
|
+
console.log({
|
|
298
|
+
level: quality.level,
|
|
299
|
+
score: quality.score,
|
|
300
|
+
gaps: quality.gaps,
|
|
301
|
+
followUp: quality.recommendedFollowUp,
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Evidence quality is also included in `buildPostureDigest()` output, so mobile and API clients can display scan confidence without loading the full result.
|
|
306
|
+
|
|
290
307
|
## Package trust and release signals
|
|
291
308
|
|
|
292
309
|
- public source repository with package code under `packages/core`
|
|
@@ -371,6 +388,7 @@ Primary exports:
|
|
|
371
388
|
- `buildPostureRemediationPlan(result)` - generate prioritized, owner-aware remediation actions from findings and score drivers.
|
|
372
389
|
- `attachIssueEvidence(result)` - add structured evidence references to findings without changing their existing fields.
|
|
373
390
|
- `buildPostureEvidenceSummary(result)` - produce compact evidence metadata for API, mobile, report, and explainability surfaces.
|
|
391
|
+
- `buildEvidenceQualitySummary(result)` - summarize scan confidence, collection gaps, and recommended follow-up.
|
|
374
392
|
|
|
375
393
|
Package subpath exports:
|
|
376
394
|
|
|
@@ -384,6 +402,7 @@ Package subpath exports:
|
|
|
384
402
|
- `securl/observation-policy`
|
|
385
403
|
- `securl/posture-drift`
|
|
386
404
|
- `securl/remediation-plan`
|
|
405
|
+
- `securl/evidence-quality`
|
|
387
406
|
- `securl/risk-events`
|
|
388
407
|
- `securl/types`
|
|
389
408
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const OBSERVED_KINDS = new Set(["header", "tls", "cookie", "redirect", "dns", "html", "public_record"]);
|
|
2
|
+
const normalizeArray = (value) => (Array.isArray(value) ? value : []);
|
|
3
|
+
function clampScore(score) {
|
|
4
|
+
return Math.max(0, Math.min(100, Math.round(score)));
|
|
5
|
+
}
|
|
6
|
+
function levelForScore(score) {
|
|
7
|
+
if (score >= 80)
|
|
8
|
+
return "high";
|
|
9
|
+
if (score >= 55)
|
|
10
|
+
return "medium";
|
|
11
|
+
return "low";
|
|
12
|
+
}
|
|
13
|
+
function pushSignal(signals, id, label, detail, impact) {
|
|
14
|
+
signals.push({ id, label, detail, impact });
|
|
15
|
+
}
|
|
16
|
+
function buildSummary(level, analysis, gaps) {
|
|
17
|
+
if (analysis.assessmentLimitation?.limited) {
|
|
18
|
+
return "Evidence quality is limited because the target did not return a complete posture read.";
|
|
19
|
+
}
|
|
20
|
+
if (level === "high") {
|
|
21
|
+
return "Evidence quality is high enough to treat the posture result as a solid outside-in read.";
|
|
22
|
+
}
|
|
23
|
+
if (level === "medium") {
|
|
24
|
+
return "Evidence quality is usable, but a few collection gaps should be considered before treating the result as final.";
|
|
25
|
+
}
|
|
26
|
+
if (gaps.length > 0) {
|
|
27
|
+
return "Evidence quality is low, so treat this result as directional until the collection gaps are resolved.";
|
|
28
|
+
}
|
|
29
|
+
return "Evidence quality is low because the scan produced too little structured support for the result.";
|
|
30
|
+
}
|
|
31
|
+
export function buildEvidenceQualitySummary(analysis) {
|
|
32
|
+
const evidenceSummary = analysis.evidenceSummary;
|
|
33
|
+
const issues = normalizeArray(analysis.issues);
|
|
34
|
+
const timing = analysis.scanTiming;
|
|
35
|
+
const totalReferences = evidenceSummary?.totalEvidenceReferences ?? 0;
|
|
36
|
+
const observedReferences = evidenceSummary?.observedCount ?? 0;
|
|
37
|
+
const derivedReferences = evidenceSummary?.derivedCount ?? 0;
|
|
38
|
+
const observedRatio = totalReferences > 0 ? observedReferences / totalReferences : 0;
|
|
39
|
+
const kinds = Object.keys(evidenceSummary?.byKind ?? {});
|
|
40
|
+
const lowConfidence = issues.filter((issue) => issue.confidence === "low").length;
|
|
41
|
+
const mediumConfidence = issues.filter((issue) => issue.confidence === "medium").length;
|
|
42
|
+
const highConfidence = issues.filter((issue) => issue.confidence === "high").length;
|
|
43
|
+
const strengths = [];
|
|
44
|
+
const gaps = [];
|
|
45
|
+
let score = 50;
|
|
46
|
+
if (totalReferences >= 12) {
|
|
47
|
+
score += 18;
|
|
48
|
+
pushSignal(strengths, "evidence_volume", "Evidence volume", "The scan produced a broad set of structured evidence references.", "positive");
|
|
49
|
+
}
|
|
50
|
+
else if (totalReferences >= 5) {
|
|
51
|
+
score += 10;
|
|
52
|
+
pushSignal(strengths, "evidence_volume", "Evidence volume", "The scan produced enough structured evidence for a useful posture read.", "positive");
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
score -= 18;
|
|
56
|
+
pushSignal(gaps, "low_evidence_volume", "Low evidence volume", "The scan produced fewer structured evidence references than expected.", "negative");
|
|
57
|
+
}
|
|
58
|
+
if (observedRatio >= 0.7) {
|
|
59
|
+
score += 16;
|
|
60
|
+
pushSignal(strengths, "observed_evidence", "Observed evidence", "Most evidence came from directly observed target data rather than derived context.", "positive");
|
|
61
|
+
}
|
|
62
|
+
else if (observedRatio >= 0.45) {
|
|
63
|
+
score += 6;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
score -= 14;
|
|
67
|
+
pushSignal(gaps, "derived_heavy", "Derived-heavy evidence", "The result relies heavily on derived evidence, so direct verification is thinner.", "negative");
|
|
68
|
+
}
|
|
69
|
+
if (kinds.length >= 5) {
|
|
70
|
+
score += 12;
|
|
71
|
+
pushSignal(strengths, "evidence_breadth", "Evidence breadth", "The scan collected evidence across several posture areas.", "positive");
|
|
72
|
+
}
|
|
73
|
+
else if (kinds.length <= 2) {
|
|
74
|
+
score -= 12;
|
|
75
|
+
pushSignal(gaps, "narrow_evidence", "Narrow evidence", "Evidence came from a small number of source types.", "negative");
|
|
76
|
+
}
|
|
77
|
+
if (analysis.assessmentLimitation?.limited) {
|
|
78
|
+
score -= 35;
|
|
79
|
+
pushSignal(gaps, "limited_assessment", "Limited assessment", analysis.assessmentLimitation.detail ?? "The target response limited collection.", "negative");
|
|
80
|
+
}
|
|
81
|
+
if (timing?.timedOut) {
|
|
82
|
+
score -= 18;
|
|
83
|
+
pushSignal(gaps, "scan_timeout", "Scan timeout", "The scan timed out before all enrichment completed.", "negative");
|
|
84
|
+
}
|
|
85
|
+
if (analysis.statusCode >= 500 || analysis.statusCode === 0) {
|
|
86
|
+
score -= 14;
|
|
87
|
+
pushSignal(gaps, "availability_status", "Availability status", `The target returned HTTP ${analysis.statusCode}, reducing confidence in the observed posture.`, "negative");
|
|
88
|
+
}
|
|
89
|
+
else if (analysis.statusCode >= 200 && analysis.statusCode < 400) {
|
|
90
|
+
score += 8;
|
|
91
|
+
pushSignal(strengths, "normal_response", "Normal response", `The target returned HTTP ${analysis.statusCode}, supporting a normal posture read.`, "positive");
|
|
92
|
+
}
|
|
93
|
+
if (lowConfidence > 0) {
|
|
94
|
+
score -= Math.min(14, lowConfidence * 4);
|
|
95
|
+
pushSignal(gaps, "low_confidence_findings", "Low-confidence findings", `${lowConfidence} finding${lowConfidence === 1 ? "" : "s"} had low confidence.`, "negative");
|
|
96
|
+
}
|
|
97
|
+
if (highConfidence > 0 && highConfidence >= lowConfidence + mediumConfidence) {
|
|
98
|
+
score += 8;
|
|
99
|
+
pushSignal(strengths, "high_confidence_findings", "High-confidence findings", "Most findings were supported with high-confidence evidence.", "positive");
|
|
100
|
+
}
|
|
101
|
+
if (!normalizeArray(analysis.headers).length) {
|
|
102
|
+
score -= 10;
|
|
103
|
+
pushSignal(gaps, "missing_header_set", "Missing header set", "No response header set was available for the scan.", "negative");
|
|
104
|
+
}
|
|
105
|
+
if (!analysis.certificate?.available) {
|
|
106
|
+
score -= 8;
|
|
107
|
+
pushSignal(gaps, "certificate_unavailable", "Certificate unavailable", "The scan could not read a served TLS certificate.", "negative");
|
|
108
|
+
}
|
|
109
|
+
else if (analysis.certificate.authorized !== false) {
|
|
110
|
+
score += 6;
|
|
111
|
+
pushSignal(strengths, "certificate_observed", "Certificate observed", "A served TLS certificate was observed during the scan.", "positive");
|
|
112
|
+
}
|
|
113
|
+
const finalScore = clampScore(score);
|
|
114
|
+
const level = levelForScore(finalScore);
|
|
115
|
+
const recommendedFollowUp = [
|
|
116
|
+
...(analysis.assessmentLimitation?.limited ? ["Restore complete scan coverage and rescan before treating the grade as final."] : []),
|
|
117
|
+
...(timing?.timedOut ? ["Rerun the scan with a longer timeout or narrower mode to complete enrichment."] : []),
|
|
118
|
+
...(observedRatio < 0.45 ? ["Verify the top findings manually because direct observed evidence is thin."] : []),
|
|
119
|
+
...(kinds.length <= 2 ? ["Collect another scan after the target returns normal headers, TLS, DNS, and HTML evidence."] : []),
|
|
120
|
+
];
|
|
121
|
+
if (recommendedFollowUp.length === 0) {
|
|
122
|
+
recommendedFollowUp.push("Use the score drivers and top insights as the primary follow-up path.");
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
generatedAt: new Date().toISOString(),
|
|
126
|
+
level,
|
|
127
|
+
score: finalScore,
|
|
128
|
+
summary: buildSummary(level, analysis, gaps),
|
|
129
|
+
evidence: {
|
|
130
|
+
totalReferences,
|
|
131
|
+
observedReferences,
|
|
132
|
+
derivedReferences,
|
|
133
|
+
observedRatio: Number(observedRatio.toFixed(2)),
|
|
134
|
+
kinds: kinds.filter((kind) => OBSERVED_KINDS.has(kind) || kind === "probe" || kind === "score_driver"),
|
|
135
|
+
},
|
|
136
|
+
scan: {
|
|
137
|
+
limited: Boolean(analysis.assessmentLimitation?.limited),
|
|
138
|
+
limitedKind: analysis.assessmentLimitation?.kind ?? null,
|
|
139
|
+
timedOut: Boolean(timing?.timedOut),
|
|
140
|
+
statusCode: analysis.statusCode,
|
|
141
|
+
responseTimeMs: analysis.responseTimeMs,
|
|
142
|
+
},
|
|
143
|
+
findings: {
|
|
144
|
+
total: issues.length,
|
|
145
|
+
lowConfidence,
|
|
146
|
+
mediumConfidence,
|
|
147
|
+
highConfidence,
|
|
148
|
+
},
|
|
149
|
+
strengths: strengths.slice(0, 5),
|
|
150
|
+
gaps: gaps.slice(0, 5),
|
|
151
|
+
recommendedFollowUp: recommendedFollowUp.slice(0, 4),
|
|
152
|
+
limitation: analysis.assessmentLimitation?.limited ? analysis.assessmentLimitation : null,
|
|
153
|
+
};
|
|
154
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { buildObservationLedger } from "./observations.js";
|
|
|
15
15
|
export { diffObservationLedgers } from "./observationDrift.js";
|
|
16
16
|
export { DEFAULT_OBSERVATION_POLICY, evaluateObservationPolicy, validateObservationPolicy } from "./observationPolicy.js";
|
|
17
17
|
export { buildActionPlan } from "./actionPlan.js";
|
|
18
|
+
export { buildEvidenceQualitySummary } from "./evidenceQuality.js";
|
|
18
19
|
export { buildPostureInsights } from "./postureInsights.js";
|
|
19
20
|
export { scanLiveCertificate } from "./certificate.js";
|
|
20
21
|
export { buildExposureBrief } from "./exposureBrief.js";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { URL } from "node:url";
|
|
|
2
2
|
import { scanTls } from "./certificate.js";
|
|
3
3
|
import { buildActionPlan } from "./actionPlan.js";
|
|
4
4
|
import { buildCompromiseSignals, emptyCompromiseSignals } from "./compromiseSignals.js";
|
|
5
|
+
import { buildEvidenceQualitySummary } from "./evidenceQuality.js";
|
|
5
6
|
import { buildExposureBrief } from "./exposureBrief.js";
|
|
6
7
|
import { buildPostureInsights } from "./postureInsights.js";
|
|
7
8
|
import { buildVendorExposureBrief } from "./vendorExposure.js";
|
|
@@ -511,10 +512,14 @@ async function buildLimitedResult(input, normalizedInput, failure, scanTiming) {
|
|
|
511
512
|
remediationPlan,
|
|
512
513
|
evidenceSummary: buildPostureEvidenceSummary({ ...evidenceResult, remediationPlan }),
|
|
513
514
|
};
|
|
514
|
-
const
|
|
515
|
+
const resultWithQuality = {
|
|
515
516
|
...resultWithRemediation,
|
|
516
|
-
|
|
517
|
-
|
|
517
|
+
evidenceQuality: buildEvidenceQualitySummary(resultWithRemediation),
|
|
518
|
+
};
|
|
519
|
+
const resultWithBriefs = {
|
|
520
|
+
...resultWithQuality,
|
|
521
|
+
exposureBrief: buildExposureBrief(resultWithQuality),
|
|
522
|
+
vendorExposure: buildVendorExposureBrief(resultWithQuality),
|
|
518
523
|
};
|
|
519
524
|
const resultWithActions = {
|
|
520
525
|
...resultWithBriefs,
|
|
@@ -958,10 +963,14 @@ function buildTimedOutEnrichmentResult(result, pageAnalysisEnabled, timeoutMs, c
|
|
|
958
963
|
remediationPlan,
|
|
959
964
|
evidenceSummary: buildPostureEvidenceSummary({ ...evidenceResult, remediationPlan }),
|
|
960
965
|
};
|
|
961
|
-
const
|
|
966
|
+
const resultWithQuality = {
|
|
962
967
|
...resultWithRemediation,
|
|
963
|
-
|
|
964
|
-
|
|
968
|
+
evidenceQuality: buildEvidenceQualitySummary(resultWithRemediation),
|
|
969
|
+
};
|
|
970
|
+
const resultWithBriefs = {
|
|
971
|
+
...resultWithQuality,
|
|
972
|
+
exposureBrief: buildExposureBrief(resultWithQuality),
|
|
973
|
+
vendorExposure: buildVendorExposureBrief(resultWithQuality),
|
|
965
974
|
};
|
|
966
975
|
const resultWithActions = {
|
|
967
976
|
...resultWithBriefs,
|
|
@@ -1044,10 +1053,14 @@ export async function analyzeUrl(input, options = {}) {
|
|
|
1044
1053
|
remediationPlan,
|
|
1045
1054
|
evidenceSummary: buildPostureEvidenceSummary({ ...resultWithEvidence, remediationPlan }),
|
|
1046
1055
|
};
|
|
1047
|
-
const
|
|
1056
|
+
const resultWithQuality = {
|
|
1048
1057
|
...resultWithRemediation,
|
|
1049
|
-
|
|
1050
|
-
|
|
1058
|
+
evidenceQuality: buildEvidenceQualitySummary(resultWithRemediation),
|
|
1059
|
+
};
|
|
1060
|
+
const resultWithBriefs = {
|
|
1061
|
+
...resultWithQuality,
|
|
1062
|
+
exposureBrief: buildExposureBrief(resultWithQuality),
|
|
1063
|
+
vendorExposure: buildVendorExposureBrief(resultWithQuality),
|
|
1051
1064
|
};
|
|
1052
1065
|
const resultWithActions = {
|
|
1053
1066
|
...resultWithBriefs,
|
|
@@ -1069,6 +1082,7 @@ export { buildObservationLedger } from "./observations.js";
|
|
|
1069
1082
|
export { diffObservationLedgers } from "./observationDrift.js";
|
|
1070
1083
|
export { DEFAULT_OBSERVATION_POLICY, evaluateObservationPolicy, validateObservationPolicy } from "./observationPolicy.js";
|
|
1071
1084
|
export { buildActionPlan } from "./actionPlan.js";
|
|
1085
|
+
export { buildEvidenceQualitySummary } from "./evidenceQuality.js";
|
|
1072
1086
|
export { buildPostureInsights } from "./postureInsights.js";
|
|
1073
1087
|
export { scanLiveCertificate } from "./certificate.js";
|
|
1074
1088
|
export { buildExposureBrief } from "./exposureBrief.js";
|
package/dist/postureDigest.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare function buildPostureDigest(analysis: AnalysisResult, { findingLi
|
|
|
20
20
|
posture: "strong" | "weak" | "mixed";
|
|
21
21
|
takeaways: string[];
|
|
22
22
|
limited: boolean;
|
|
23
|
-
limitedKind: "
|
|
23
|
+
limitedKind: "blocked_edge_response" | "auth_required" | "rate_limited" | "service_unavailable" | "other";
|
|
24
24
|
limitation: import("./types.js").AssessmentLimitation;
|
|
25
25
|
scoreDrivers: import("./types.js").ScoreDriver[];
|
|
26
26
|
};
|
|
@@ -59,6 +59,34 @@ export declare function buildPostureDigest(analysis: AnalysisResult, { findingLi
|
|
|
59
59
|
scoreImpact: number;
|
|
60
60
|
}[];
|
|
61
61
|
};
|
|
62
|
+
evidenceQuality: {
|
|
63
|
+
level: import("./types.js").EvidenceQualityLevel;
|
|
64
|
+
score: number;
|
|
65
|
+
summary: string;
|
|
66
|
+
evidence: {
|
|
67
|
+
totalReferences: number;
|
|
68
|
+
observedReferences: number;
|
|
69
|
+
derivedReferences: number;
|
|
70
|
+
observedRatio: number;
|
|
71
|
+
kinds: import("./types.js").ScanEvidenceKind[];
|
|
72
|
+
};
|
|
73
|
+
scan: {
|
|
74
|
+
limited: boolean;
|
|
75
|
+
limitedKind: import("./types.js").AssessmentLimitation["kind"];
|
|
76
|
+
timedOut: boolean;
|
|
77
|
+
statusCode: number;
|
|
78
|
+
responseTimeMs: number;
|
|
79
|
+
};
|
|
80
|
+
findings: {
|
|
81
|
+
total: number;
|
|
82
|
+
lowConfidence: number;
|
|
83
|
+
mediumConfidence: number;
|
|
84
|
+
highConfidence: number;
|
|
85
|
+
};
|
|
86
|
+
strengths: import("./types.js").EvidenceQualitySignal[];
|
|
87
|
+
gaps: import("./types.js").EvidenceQualitySignal[];
|
|
88
|
+
recommendedFollowUp: string[];
|
|
89
|
+
};
|
|
62
90
|
remediationPlan: {
|
|
63
91
|
summary: string;
|
|
64
92
|
totalActions: number;
|
package/dist/postureDigest.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildEvidenceQualitySummary } from "./evidenceQuality.js";
|
|
1
2
|
const SEVERITY_ORDER = {
|
|
2
3
|
critical: 0,
|
|
3
4
|
warning: 1,
|
|
@@ -31,6 +32,7 @@ export function buildPostureDigest(analysis, { findingLimit = 8 } = {}) {
|
|
|
31
32
|
const issues = normalizeArray(analysis.issues);
|
|
32
33
|
const compromiseIndicators = normalizeArray(analysis.compromiseSignals?.indicators);
|
|
33
34
|
const riskIndicators = compromiseIndicators.filter((indicator) => ["warning", "critical"].includes(indicator.severity));
|
|
35
|
+
const evidenceQuality = analysis.evidenceQuality ?? buildEvidenceQualitySummary(analysis);
|
|
34
36
|
return {
|
|
35
37
|
generatedAt: new Date().toISOString(),
|
|
36
38
|
target: {
|
|
@@ -77,6 +79,17 @@ export function buildPostureDigest(analysis, { findingLimit = 8 } = {}) {
|
|
|
77
79
|
scoreImpact: reference.scoreImpact,
|
|
78
80
|
})),
|
|
79
81
|
} : null,
|
|
82
|
+
evidenceQuality: {
|
|
83
|
+
level: evidenceQuality.level,
|
|
84
|
+
score: evidenceQuality.score,
|
|
85
|
+
summary: evidenceQuality.summary,
|
|
86
|
+
evidence: evidenceQuality.evidence,
|
|
87
|
+
scan: evidenceQuality.scan,
|
|
88
|
+
findings: evidenceQuality.findings,
|
|
89
|
+
strengths: normalizeArray(evidenceQuality.strengths).slice(0, 5),
|
|
90
|
+
gaps: normalizeArray(evidenceQuality.gaps).slice(0, 5),
|
|
91
|
+
recommendedFollowUp: normalizeArray(evidenceQuality.recommendedFollowUp).slice(0, 4),
|
|
92
|
+
},
|
|
80
93
|
remediationPlan: analysis.remediationPlan ? {
|
|
81
94
|
summary: analysis.remediationPlan.summary,
|
|
82
95
|
totalActions: analysis.remediationPlan.totalActions,
|
package/dist/types.d.ts
CHANGED
|
@@ -172,6 +172,43 @@ export interface PostureEvidenceSummary {
|
|
|
172
172
|
findingEvidence: PostureEvidenceSummaryReference[];
|
|
173
173
|
limitation: AssessmentLimitation | null;
|
|
174
174
|
}
|
|
175
|
+
export type EvidenceQualityLevel = "high" | "medium" | "low";
|
|
176
|
+
export interface EvidenceQualitySignal {
|
|
177
|
+
id: string;
|
|
178
|
+
label: string;
|
|
179
|
+
detail: string;
|
|
180
|
+
impact: "positive" | "negative" | "neutral";
|
|
181
|
+
}
|
|
182
|
+
export interface EvidenceQualitySummary {
|
|
183
|
+
generatedAt: string;
|
|
184
|
+
level: EvidenceQualityLevel;
|
|
185
|
+
score: number;
|
|
186
|
+
summary: string;
|
|
187
|
+
evidence: {
|
|
188
|
+
totalReferences: number;
|
|
189
|
+
observedReferences: number;
|
|
190
|
+
derivedReferences: number;
|
|
191
|
+
observedRatio: number;
|
|
192
|
+
kinds: ScanEvidenceKind[];
|
|
193
|
+
};
|
|
194
|
+
scan: {
|
|
195
|
+
limited: boolean;
|
|
196
|
+
limitedKind: AssessmentLimitation["kind"];
|
|
197
|
+
timedOut: boolean;
|
|
198
|
+
statusCode: number;
|
|
199
|
+
responseTimeMs: number;
|
|
200
|
+
};
|
|
201
|
+
findings: {
|
|
202
|
+
total: number;
|
|
203
|
+
lowConfidence: number;
|
|
204
|
+
mediumConfidence: number;
|
|
205
|
+
highConfidence: number;
|
|
206
|
+
};
|
|
207
|
+
strengths: EvidenceQualitySignal[];
|
|
208
|
+
gaps: EvidenceQualitySignal[];
|
|
209
|
+
recommendedFollowUp: string[];
|
|
210
|
+
limitation: AssessmentLimitation | null;
|
|
211
|
+
}
|
|
175
212
|
export type ExposureBriefLevel = "low" | "medium" | "high" | "critical" | "unknown";
|
|
176
213
|
export type ExposureBriefCategory = "entry_point" | "trust_gap" | "abuse_signal" | "sensitive_exposure" | "third_party" | "identity" | "ai" | "infrastructure";
|
|
177
214
|
export type ExposureBriefSource = "headers" | "tls" | "cookies" | "dns" | "html" | "public_record" | "third_party" | "ai" | "ct" | "api" | "exposure" | "derived";
|
|
@@ -1008,6 +1045,7 @@ export interface AnalysisResult {
|
|
|
1008
1045
|
remediation: RemediationSnippet[];
|
|
1009
1046
|
remediationPlan?: RemediationPlan;
|
|
1010
1047
|
evidenceSummary?: PostureEvidenceSummary;
|
|
1048
|
+
evidenceQuality?: EvidenceQualitySummary;
|
|
1011
1049
|
exposureBrief?: ExposureBrief;
|
|
1012
1050
|
vendorExposure?: VendorExposureBrief;
|
|
1013
1051
|
actionPlan?: ActionPlan;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securl",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Passive external security posture scanner for public URLs and web services.",
|
|
6
6
|
"author": {
|
|
@@ -66,6 +66,10 @@
|
|
|
66
66
|
"types": "./dist/postureRemediation.d.ts",
|
|
67
67
|
"default": "./dist/postureRemediation.js"
|
|
68
68
|
},
|
|
69
|
+
"./evidence-quality": {
|
|
70
|
+
"types": "./dist/evidenceQuality.d.ts",
|
|
71
|
+
"default": "./dist/evidenceQuality.js"
|
|
72
|
+
},
|
|
69
73
|
"./exposure-brief": {
|
|
70
74
|
"types": "./dist/exposureBrief.d.ts",
|
|
71
75
|
"default": "./dist/exposureBrief.js"
|