securl 1.5.1 → 1.6.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 +4 -0
- package/dist/cli.js +0 -0
- package/dist/exposureBrief.d.ts +2 -0
- package/dist/exposureBrief.js +278 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +17 -3
- package/dist/types.d.ts +35 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,10 @@ The format is based on Keep a Changelog and this package follows Semantic Versio
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
### Added
|
|
10
|
+
- Added `buildExposureBrief()` for compact outside-observer action briefs covering public entry points, sensitive exposures, trust gaps, abuse indicators, third-party risk, AI surface signals, and next actions.
|
|
11
|
+
- Added `exposureBrief` to analysis results and the `securl/exposure-brief` package export for SDK consumers.
|
|
12
|
+
|
|
9
13
|
## [1.5.1] - 2026-06-15
|
|
10
14
|
|
|
11
15
|
### Changed
|
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
const SEVERITY_ORDER = {
|
|
2
|
+
critical: 0,
|
|
3
|
+
warning: 1,
|
|
4
|
+
watch: 2,
|
|
5
|
+
info: 3,
|
|
6
|
+
};
|
|
7
|
+
const normalizeArray = (value) => (Array.isArray(value) ? value : []);
|
|
8
|
+
function cleanEvidence(value) {
|
|
9
|
+
return normalizeArray(value)
|
|
10
|
+
.filter((item) => typeof item === "string" && item.trim().length > 0)
|
|
11
|
+
.map((item) => item.trim())
|
|
12
|
+
.slice(0, 4);
|
|
13
|
+
}
|
|
14
|
+
function uniqueByTitle(items) {
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
const unique = [];
|
|
17
|
+
for (const item of items) {
|
|
18
|
+
const key = `${item.category}:${item.title.toLowerCase()}:${item.detail.toLowerCase()}`;
|
|
19
|
+
if (seen.has(key)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
seen.add(key);
|
|
23
|
+
unique.push(item);
|
|
24
|
+
}
|
|
25
|
+
return unique;
|
|
26
|
+
}
|
|
27
|
+
function sortItems(items) {
|
|
28
|
+
return [...items].sort((left, right) => {
|
|
29
|
+
const severityDelta = SEVERITY_ORDER[left.severity] - SEVERITY_ORDER[right.severity];
|
|
30
|
+
if (severityDelta !== 0) {
|
|
31
|
+
return severityDelta;
|
|
32
|
+
}
|
|
33
|
+
return left.title.localeCompare(right.title);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function item({ title, detail, severity, category, confidence = "medium", source, evidence = [], action, }) {
|
|
37
|
+
return {
|
|
38
|
+
title,
|
|
39
|
+
detail,
|
|
40
|
+
severity,
|
|
41
|
+
category,
|
|
42
|
+
confidence,
|
|
43
|
+
source,
|
|
44
|
+
evidence: cleanEvidence(evidence),
|
|
45
|
+
action,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function mapCompromiseCategory(indicator) {
|
|
49
|
+
if (indicator.category === "credential_collection" || indicator.category === "script_anomaly") {
|
|
50
|
+
return "abuse_signal";
|
|
51
|
+
}
|
|
52
|
+
if (indicator.category === "supply_chain") {
|
|
53
|
+
return "third_party";
|
|
54
|
+
}
|
|
55
|
+
if (indicator.category === "infrastructure") {
|
|
56
|
+
return "infrastructure";
|
|
57
|
+
}
|
|
58
|
+
if (indicator.category === "exposure") {
|
|
59
|
+
return "sensitive_exposure";
|
|
60
|
+
}
|
|
61
|
+
return "abuse_signal";
|
|
62
|
+
}
|
|
63
|
+
function mapCompromiseSource(indicator) {
|
|
64
|
+
if (indicator.source === "asset") {
|
|
65
|
+
return "html";
|
|
66
|
+
}
|
|
67
|
+
if (indicator.source === "reputation") {
|
|
68
|
+
return "public_record";
|
|
69
|
+
}
|
|
70
|
+
return indicator.source;
|
|
71
|
+
}
|
|
72
|
+
function buildSummary(level, counts, topRisks) {
|
|
73
|
+
if (level === "unknown") {
|
|
74
|
+
return "Exposure could not be summarized confidently because the passive assessment was limited.";
|
|
75
|
+
}
|
|
76
|
+
if (level === "critical") {
|
|
77
|
+
return "Critical public exposure or abuse indicators need immediate review before treating this target as healthy.";
|
|
78
|
+
}
|
|
79
|
+
if (level === "high") {
|
|
80
|
+
return "Publicly observable exposure is elevated, with multiple items that deserve near-term review.";
|
|
81
|
+
}
|
|
82
|
+
if (level === "medium") {
|
|
83
|
+
return "The target has review-worthy public exposure, but no critical public signal was observed.";
|
|
84
|
+
}
|
|
85
|
+
if (counts.publicEntryPoints > 0) {
|
|
86
|
+
return "Public entry points were observed, with no major exposure signal in the passive checks.";
|
|
87
|
+
}
|
|
88
|
+
if (topRisks.length === 0) {
|
|
89
|
+
return "No notable public exposure signal was observed in the passive checks.";
|
|
90
|
+
}
|
|
91
|
+
return "The passive checks found low-risk public exposure context.";
|
|
92
|
+
}
|
|
93
|
+
function deriveLevel(items, counts, limited) {
|
|
94
|
+
if (items.some((risk) => risk.severity === "critical")) {
|
|
95
|
+
return "critical";
|
|
96
|
+
}
|
|
97
|
+
const warningCount = items.filter((risk) => risk.severity === "warning").length;
|
|
98
|
+
if (warningCount >= 3 || counts.sensitiveExposures > 0 || counts.highRiskThirdParties > 0) {
|
|
99
|
+
return "high";
|
|
100
|
+
}
|
|
101
|
+
if (warningCount > 0 || items.some((risk) => risk.severity === "watch")) {
|
|
102
|
+
return "medium";
|
|
103
|
+
}
|
|
104
|
+
if (limited && items.length === 0) {
|
|
105
|
+
return "unknown";
|
|
106
|
+
}
|
|
107
|
+
return "low";
|
|
108
|
+
}
|
|
109
|
+
function pushUnique(actions, value) {
|
|
110
|
+
if (!value) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const trimmed = value.trim();
|
|
114
|
+
if (!trimmed || actions.includes(trimmed)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
actions.push(trimmed);
|
|
118
|
+
}
|
|
119
|
+
export function buildExposureBrief(analysis) {
|
|
120
|
+
const items = [];
|
|
121
|
+
for (const indicator of normalizeArray(analysis.compromiseSignals?.indicators)) {
|
|
122
|
+
items.push(item({
|
|
123
|
+
title: indicator.title,
|
|
124
|
+
detail: indicator.detail,
|
|
125
|
+
severity: indicator.severity,
|
|
126
|
+
category: mapCompromiseCategory(indicator),
|
|
127
|
+
confidence: indicator.confidence,
|
|
128
|
+
source: mapCompromiseSource(indicator),
|
|
129
|
+
evidence: indicator.evidence,
|
|
130
|
+
action: indicator.action,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
for (const probe of normalizeArray(analysis.exposure?.probes)) {
|
|
134
|
+
if (probe.finding !== "exposed" && probe.finding !== "interesting") {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
items.push(item({
|
|
138
|
+
title: probe.finding === "exposed" ? `${probe.label} appears exposed` : `${probe.label} needs review`,
|
|
139
|
+
detail: probe.detail,
|
|
140
|
+
severity: probe.finding === "exposed" ? "warning" : "watch",
|
|
141
|
+
category: "sensitive_exposure",
|
|
142
|
+
confidence: "medium",
|
|
143
|
+
source: "exposure",
|
|
144
|
+
evidence: [`${probe.statusCode} ${probe.finalUrl}`],
|
|
145
|
+
action: "Review whether this path should be publicly reachable and restrict it if it exposes operational detail.",
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
for (const probe of normalizeArray(analysis.apiSurface?.probes)) {
|
|
149
|
+
if (probe.classification !== "public" && probe.classification !== "interesting") {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
items.push(item({
|
|
153
|
+
title: probe.classification === "public" ? `${probe.label} is publicly reachable` : `${probe.label} looks like an API surface`,
|
|
154
|
+
detail: probe.detail,
|
|
155
|
+
severity: probe.classification === "public" ? "watch" : "info",
|
|
156
|
+
category: "entry_point",
|
|
157
|
+
confidence: "medium",
|
|
158
|
+
source: "api",
|
|
159
|
+
evidence: [`${probe.statusCode} ${probe.finalUrl}`, probe.contentType ? `Content-Type: ${probe.contentType}` : ""],
|
|
160
|
+
action: "Confirm the endpoint is intentional, authenticated where needed, and covered by monitoring.",
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
for (const host of normalizeArray(analysis.ctDiscovery?.prioritizedHosts).slice(0, 8)) {
|
|
164
|
+
const priority = "priority" in host ? String(host.priority) : "review";
|
|
165
|
+
items.push(item({
|
|
166
|
+
title: `${host.host} is visible in certificate transparency`,
|
|
167
|
+
detail: "Certificate transparency logs expose this related host as part of the public attack surface.",
|
|
168
|
+
severity: priority === "high" ? "watch" : "info",
|
|
169
|
+
category: "entry_point",
|
|
170
|
+
confidence: "high",
|
|
171
|
+
source: "ct",
|
|
172
|
+
evidence: [analysis.ctDiscovery?.sourceUrl || "", priority],
|
|
173
|
+
action: "Confirm this hostname is still owned, intentionally exposed, and included in monitoring.",
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
for (const issue of normalizeArray(analysis.domainSecurity?.issues)) {
|
|
177
|
+
items.push(item({
|
|
178
|
+
title: "Domain trust gap",
|
|
179
|
+
detail: issue,
|
|
180
|
+
severity: "watch",
|
|
181
|
+
category: "trust_gap",
|
|
182
|
+
confidence: "high",
|
|
183
|
+
source: "dns",
|
|
184
|
+
evidence: [analysis.host],
|
|
185
|
+
action: "Review DNS, mail authentication, and domain policy records for the target domain.",
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
for (const issue of normalizeArray(analysis.securityTxt?.issues)) {
|
|
189
|
+
items.push(item({
|
|
190
|
+
title: "Security contact signal gap",
|
|
191
|
+
detail: issue,
|
|
192
|
+
severity: "info",
|
|
193
|
+
category: "trust_gap",
|
|
194
|
+
confidence: "medium",
|
|
195
|
+
source: "public_record",
|
|
196
|
+
evidence: [analysis.securityTxt?.url || analysis.finalUrl],
|
|
197
|
+
action: "Publish or correct security.txt so researchers and vendors know where to report issues.",
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
for (const issue of normalizeArray(analysis.publicSignals?.issues)) {
|
|
201
|
+
items.push(item({
|
|
202
|
+
title: "Public trust signal gap",
|
|
203
|
+
detail: issue,
|
|
204
|
+
severity: "watch",
|
|
205
|
+
category: "trust_gap",
|
|
206
|
+
confidence: "medium",
|
|
207
|
+
source: "public_record",
|
|
208
|
+
evidence: [analysis.publicSignals?.hstsPreload?.sourceUrl || analysis.host],
|
|
209
|
+
action: "Review public trust signals such as HSTS preload eligibility and domain policy posture.",
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
for (const provider of normalizeArray(analysis.thirdPartyTrust?.providers)) {
|
|
213
|
+
if (provider.risk !== "high" && provider.risk !== "medium") {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
items.push(item({
|
|
217
|
+
title: `${provider.name} third-party dependency`,
|
|
218
|
+
detail: `${provider.domain} was observed as a ${provider.category} provider with ${provider.risk} passive trust risk.`,
|
|
219
|
+
severity: provider.risk === "high" ? "warning" : "watch",
|
|
220
|
+
category: "third_party",
|
|
221
|
+
confidence: "medium",
|
|
222
|
+
source: "third_party",
|
|
223
|
+
evidence: [provider.evidence],
|
|
224
|
+
action: "Confirm the provider is intentional, documented, and covered by privacy/security review.",
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
for (const vendor of normalizeArray(analysis.aiSurface?.vendors)) {
|
|
228
|
+
items.push(item({
|
|
229
|
+
title: `${vendor.name} AI surface signal`,
|
|
230
|
+
detail: vendor.evidence,
|
|
231
|
+
severity: "info",
|
|
232
|
+
category: "ai",
|
|
233
|
+
confidence: vendor.confidence,
|
|
234
|
+
source: "ai",
|
|
235
|
+
evidence: [vendor.category],
|
|
236
|
+
action: "Confirm AI-assisted surfaces have suitable disclosure, data-handling, and support escalation controls.",
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
const uniqueItems = sortItems(uniqueByTitle(items));
|
|
240
|
+
const publicEntryPoints = uniqueItems.filter((risk) => risk.category === "entry_point").slice(0, 8);
|
|
241
|
+
const trustGaps = uniqueItems.filter((risk) => risk.category === "trust_gap").slice(0, 8);
|
|
242
|
+
const topRisks = uniqueItems.slice(0, 8);
|
|
243
|
+
const counts = {
|
|
244
|
+
publicEntryPoints: publicEntryPoints.length,
|
|
245
|
+
sensitiveExposures: uniqueItems.filter((risk) => risk.category === "sensitive_exposure").length,
|
|
246
|
+
trustGaps: uniqueItems.filter((risk) => risk.category === "trust_gap").length,
|
|
247
|
+
abuseIndicators: uniqueItems.filter((risk) => risk.category === "abuse_signal").length,
|
|
248
|
+
thirdPartyProviders: analysis.thirdPartyTrust?.totalProviders ?? normalizeArray(analysis.thirdPartyTrust?.providers).length,
|
|
249
|
+
highRiskThirdParties: analysis.thirdPartyTrust?.highRiskProviders ?? 0,
|
|
250
|
+
aiVendors: normalizeArray(analysis.aiSurface?.vendors).length,
|
|
251
|
+
ctPriorityHosts: normalizeArray(analysis.ctDiscovery?.prioritizedHosts).length,
|
|
252
|
+
};
|
|
253
|
+
const exposureLevel = deriveLevel(uniqueItems, counts, Boolean(analysis.assessmentLimitation?.limited));
|
|
254
|
+
const nextActions = [];
|
|
255
|
+
for (const risk of topRisks) {
|
|
256
|
+
pushUnique(nextActions, risk.action);
|
|
257
|
+
}
|
|
258
|
+
for (const action of normalizeArray(analysis.remediationPlan?.items).map((planItem) => planItem.action)) {
|
|
259
|
+
pushUnique(nextActions, action);
|
|
260
|
+
}
|
|
261
|
+
if (nextActions.length === 0) {
|
|
262
|
+
nextActions.push("Keep the target in monitoring and rescan after meaningful deployment, DNS, or vendor changes.");
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
generatedAt: new Date().toISOString(),
|
|
266
|
+
exposureLevel,
|
|
267
|
+
summary: buildSummary(exposureLevel, counts, topRisks),
|
|
268
|
+
counts,
|
|
269
|
+
topRisks,
|
|
270
|
+
publicEntryPoints,
|
|
271
|
+
trustGaps,
|
|
272
|
+
nextActions: nextActions.slice(0, 6),
|
|
273
|
+
collectionBoundary: analysis.compromiseSignals?.collectionBoundary
|
|
274
|
+
|| analysis.passiveIntelligence?.collectionBoundary
|
|
275
|
+
|| "Passive public evidence only. No credentials, exploitation, intrusive probing, or authenticated access was used.",
|
|
276
|
+
limitation: analysis.assessmentLimitation?.limited ? analysis.assessmentLimitation : null,
|
|
277
|
+
};
|
|
278
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare function analyzeUrl(input: string, options?: AnalyzeTargetOptions
|
|
|
11
11
|
export declare const analyzeTarget: typeof analyzeUrl;
|
|
12
12
|
export { formatErrorMessage };
|
|
13
13
|
export { buildCompromiseSignals, emptyCompromiseSignals } from "./compromiseSignals.js";
|
|
14
|
+
export { buildExposureBrief } from "./exposureBrief.js";
|
|
14
15
|
export { analyzeInfrastructure } from "./infrastructure.js";
|
|
15
16
|
export { buildHistoryDiff, buildHistoryDiffFromSnapshots, snapshotFromAnalysis } from "./historyDiff.js";
|
|
16
17
|
export { assertPublicRequestTarget, isLocalHostname, isPrivateAddress, } from "./network-validation.js";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { URL } from "node:url";
|
|
2
2
|
import { scanTls } from "./certificate.js";
|
|
3
3
|
import { buildCompromiseSignals, emptyCompromiseSignals } from "./compromiseSignals.js";
|
|
4
|
+
import { buildExposureBrief } from "./exposureBrief.js";
|
|
4
5
|
import { parseSetCookie } from "./cookie-analysis.js";
|
|
5
6
|
import { analyzeCookieHeaders } from "./cookieAnalysis.js";
|
|
6
7
|
import { fetchCtDiscovery } from "./ctDiscovery.js";
|
|
@@ -501,11 +502,15 @@ async function buildLimitedResult(input, normalizedInput, failure, scanTiming) {
|
|
|
501
502
|
};
|
|
502
503
|
const evidenceResult = attachIssueEvidence(limitedResult);
|
|
503
504
|
const remediationPlan = buildPostureRemediationPlan(evidenceResult);
|
|
504
|
-
|
|
505
|
+
const resultWithRemediation = {
|
|
505
506
|
...evidenceResult,
|
|
506
507
|
remediationPlan,
|
|
507
508
|
evidenceSummary: buildPostureEvidenceSummary({ ...evidenceResult, remediationPlan }),
|
|
508
509
|
};
|
|
510
|
+
return {
|
|
511
|
+
...resultWithRemediation,
|
|
512
|
+
exposureBrief: buildExposureBrief(resultWithRemediation),
|
|
513
|
+
};
|
|
509
514
|
}
|
|
510
515
|
async function enrichCoreResult(result, profile) {
|
|
511
516
|
const finalUrl = new URL(result.finalUrl);
|
|
@@ -931,11 +936,15 @@ function buildTimedOutEnrichmentResult(result, pageAnalysisEnabled, timeoutMs, c
|
|
|
931
936
|
};
|
|
932
937
|
const evidenceResult = attachIssueEvidence(timedOutResultWithSummary);
|
|
933
938
|
const remediationPlan = buildPostureRemediationPlan(evidenceResult);
|
|
934
|
-
|
|
939
|
+
const resultWithRemediation = {
|
|
935
940
|
...evidenceResult,
|
|
936
941
|
remediationPlan,
|
|
937
942
|
evidenceSummary: buildPostureEvidenceSummary({ ...evidenceResult, remediationPlan }),
|
|
938
943
|
};
|
|
944
|
+
return {
|
|
945
|
+
...resultWithRemediation,
|
|
946
|
+
exposureBrief: buildExposureBrief(resultWithRemediation),
|
|
947
|
+
};
|
|
939
948
|
}
|
|
940
949
|
export async function analyzeUrl(input, options = {}) {
|
|
941
950
|
const scanStartedAt = Date.now();
|
|
@@ -1000,15 +1009,20 @@ export async function analyzeUrl(input, options = {}) {
|
|
|
1000
1009
|
};
|
|
1001
1010
|
const resultWithEvidence = attachIssueEvidence(resultWithSummary);
|
|
1002
1011
|
const remediationPlan = buildPostureRemediationPlan(resultWithEvidence);
|
|
1003
|
-
|
|
1012
|
+
const resultWithRemediation = {
|
|
1004
1013
|
...resultWithEvidence,
|
|
1005
1014
|
remediationPlan,
|
|
1006
1015
|
evidenceSummary: buildPostureEvidenceSummary({ ...resultWithEvidence, remediationPlan }),
|
|
1007
1016
|
};
|
|
1017
|
+
return {
|
|
1018
|
+
...resultWithRemediation,
|
|
1019
|
+
exposureBrief: buildExposureBrief(resultWithRemediation),
|
|
1020
|
+
};
|
|
1008
1021
|
}
|
|
1009
1022
|
export const analyzeTarget = analyzeUrl;
|
|
1010
1023
|
export { formatErrorMessage };
|
|
1011
1024
|
export { buildCompromiseSignals, emptyCompromiseSignals } from "./compromiseSignals.js";
|
|
1025
|
+
export { buildExposureBrief } from "./exposureBrief.js";
|
|
1012
1026
|
export { analyzeInfrastructure } from "./infrastructure.js";
|
|
1013
1027
|
export { buildHistoryDiff, buildHistoryDiffFromSnapshots, snapshotFromAnalysis } from "./historyDiff.js";
|
|
1014
1028
|
export { assertPublicRequestTarget, isLocalHostname, isPrivateAddress, } from "./network-validation.js";
|
package/dist/types.d.ts
CHANGED
|
@@ -156,6 +156,40 @@ export interface PostureEvidenceSummary {
|
|
|
156
156
|
findingEvidence: PostureEvidenceSummaryReference[];
|
|
157
157
|
limitation: AssessmentLimitation | null;
|
|
158
158
|
}
|
|
159
|
+
export type ExposureBriefLevel = "low" | "medium" | "high" | "critical" | "unknown";
|
|
160
|
+
export type ExposureBriefCategory = "entry_point" | "trust_gap" | "abuse_signal" | "sensitive_exposure" | "third_party" | "identity" | "ai" | "infrastructure";
|
|
161
|
+
export type ExposureBriefSource = "headers" | "tls" | "cookies" | "dns" | "html" | "public_record" | "third_party" | "ai" | "ct" | "api" | "exposure" | "derived";
|
|
162
|
+
export interface ExposureBriefItem {
|
|
163
|
+
title: string;
|
|
164
|
+
detail: string;
|
|
165
|
+
severity: "info" | "watch" | "warning" | "critical";
|
|
166
|
+
category: ExposureBriefCategory;
|
|
167
|
+
confidence: IssueConfidence;
|
|
168
|
+
source: ExposureBriefSource;
|
|
169
|
+
evidence: string[];
|
|
170
|
+
action: string | null;
|
|
171
|
+
}
|
|
172
|
+
export interface ExposureBrief {
|
|
173
|
+
generatedAt: string;
|
|
174
|
+
exposureLevel: ExposureBriefLevel;
|
|
175
|
+
summary: string;
|
|
176
|
+
counts: {
|
|
177
|
+
publicEntryPoints: number;
|
|
178
|
+
sensitiveExposures: number;
|
|
179
|
+
trustGaps: number;
|
|
180
|
+
abuseIndicators: number;
|
|
181
|
+
thirdPartyProviders: number;
|
|
182
|
+
highRiskThirdParties: number;
|
|
183
|
+
aiVendors: number;
|
|
184
|
+
ctPriorityHosts: number;
|
|
185
|
+
};
|
|
186
|
+
topRisks: ExposureBriefItem[];
|
|
187
|
+
publicEntryPoints: ExposureBriefItem[];
|
|
188
|
+
trustGaps: ExposureBriefItem[];
|
|
189
|
+
nextActions: string[];
|
|
190
|
+
collectionBoundary: string;
|
|
191
|
+
limitation: AssessmentLimitation | null;
|
|
192
|
+
}
|
|
159
193
|
export interface CrawlPageResult {
|
|
160
194
|
label: string;
|
|
161
195
|
path: string;
|
|
@@ -721,6 +755,7 @@ export interface AnalysisResult {
|
|
|
721
755
|
remediation: RemediationSnippet[];
|
|
722
756
|
remediationPlan?: RemediationPlan;
|
|
723
757
|
evidenceSummary?: PostureEvidenceSummary;
|
|
758
|
+
exposureBrief?: ExposureBrief;
|
|
724
759
|
crawl: CrawlSummary;
|
|
725
760
|
securityTxt: SecurityTxtInfo;
|
|
726
761
|
domainSecurity: DomainSecurityInfo;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securl",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Passive external security posture scanner for public URLs and web services.",
|
|
6
6
|
"author": {
|
|
@@ -65,6 +65,10 @@
|
|
|
65
65
|
"./evidence-summary": {
|
|
66
66
|
"types": "./dist/postureRemediation.d.ts",
|
|
67
67
|
"default": "./dist/postureRemediation.js"
|
|
68
|
+
},
|
|
69
|
+
"./exposure-brief": {
|
|
70
|
+
"types": "./dist/exposureBrief.d.ts",
|
|
71
|
+
"default": "./dist/exposureBrief.js"
|
|
68
72
|
}
|
|
69
73
|
},
|
|
70
74
|
"files": [
|