@secureport/core 2.1.0 → 2.3.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/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/issue.d.ts +1 -1
- package/dist/issue.d.ts.map +1 -1
- package/dist/reconcile.d.ts +46 -1
- package/dist/reconcile.d.ts.map +1 -1
- package/dist/reconcile.js +34 -1
- package/dist/reconcile.js.map +1 -1
- package/dist/report/anchors.d.ts +39 -0
- package/dist/report/anchors.d.ts.map +1 -0
- package/dist/report/anchors.js +73 -0
- package/dist/report/anchors.js.map +1 -0
- package/dist/report/csv.d.ts +41 -0
- package/dist/report/csv.d.ts.map +1 -0
- package/dist/report/csv.js +152 -0
- package/dist/report/csv.js.map +1 -0
- package/dist/report/html.d.ts.map +1 -1
- package/dist/report/html.js +126 -22
- package/dist/report/html.js.map +1 -1
- package/dist/report/json.d.ts +15 -0
- package/dist/report/json.d.ts.map +1 -1
- package/dist/report/json.js +1 -0
- package/dist/report/json.js.map +1 -1
- package/dist/report/markdown.d.ts +24 -2
- package/dist/report/markdown.d.ts.map +1 -1
- package/dist/report/markdown.js +133 -31
- package/dist/report/markdown.js.map +1 -1
- package/dist/report/model.d.ts +288 -2
- package/dist/report/model.d.ts.map +1 -1
- package/dist/report/model.js +153 -21
- package/dist/report/model.js.map +1 -1
- package/dist/report/sarif.d.ts +60 -0
- package/dist/report/sarif.d.ts.map +1 -0
- package/dist/report/sarif.js +125 -0
- package/dist/report/sarif.js.map +1 -0
- package/dist/report/stream.d.ts +108 -0
- package/dist/report/stream.d.ts.map +1 -0
- package/dist/report/stream.js +534 -0
- package/dist/report/stream.js.map +1 -0
- package/package.json +4 -2
- package/src/index.ts +11 -2
- package/src/issue.ts +1 -0
- package/src/reconcile.ts +86 -2
- package/src/report/anchors.ts +96 -0
- package/src/report/csv.ts +180 -0
- package/src/report/html.ts +146 -26
- package/src/report/json.ts +17 -0
- package/src/report/markdown.ts +146 -30
- package/src/report/model.ts +433 -21
- package/src/report/sarif.ts +166 -0
- package/src/report/stream.ts +702 -0
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import { SEVERITY_ORDER } from '../severity.js';
|
|
2
|
+
import { cell, formatDate, issueDetail, issueRow } from './markdown.js';
|
|
3
|
+
import { brandingProblem, describeBasis, describeLimitations, DEFAULT_TITLES, resolveAttestor, verdictFor, VERDICT_LABELS, } from './model.js';
|
|
4
|
+
function newFoldState(hasBaseline) {
|
|
5
|
+
return {
|
|
6
|
+
includesManual: false,
|
|
7
|
+
breached: 0,
|
|
8
|
+
omittedBelowFloor: 0,
|
|
9
|
+
suppressedCount: 0,
|
|
10
|
+
...(hasBaseline
|
|
11
|
+
? { retestCounts: { fixed: 0, still_present: 0, returned: 0, not_retested: 0, new: 0 } }
|
|
12
|
+
: {}),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** `"key":value`, both run through `JSON.stringify` so nothing is hand-escaped. */
|
|
16
|
+
function field(key, value) {
|
|
17
|
+
return `${JSON.stringify(key)}:${JSON.stringify(value)}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Open+regressed issues by severity, and their total — read straight off
|
|
21
|
+
* `RunSummary`, needing no pass over the issues at all.
|
|
22
|
+
*
|
|
23
|
+
* `new[sev] + stillOpen[sev] + regressed[sev]` is exactly what
|
|
24
|
+
* `buildReportModel` recomputes by iterating every issue, because every
|
|
25
|
+
* non-suppressed, non-resolved issue is counted into exactly one of those
|
|
26
|
+
* three buckets by `reconcile()` — verified equal on the shared fixture
|
|
27
|
+
* before either streaming renderer relied on it (`report-stream.test.ts`).
|
|
28
|
+
*/
|
|
29
|
+
function outstandingFrom(run) {
|
|
30
|
+
const by = {
|
|
31
|
+
critical: run.new.critical + run.stillOpen.critical + run.regressed.critical,
|
|
32
|
+
high: run.new.high + run.stillOpen.high + run.regressed.high,
|
|
33
|
+
medium: run.new.medium + run.stillOpen.medium + run.regressed.medium,
|
|
34
|
+
low: run.new.low + run.stillOpen.low + run.regressed.low,
|
|
35
|
+
advisory: run.new.advisory + run.stillOpen.advisory + run.regressed.advisory,
|
|
36
|
+
};
|
|
37
|
+
return { by, total: SEVERITY_ORDER.reduce((sum, s) => sum + by[s], 0) };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* What a severity floor kept out of a streamed document, from the count a
|
|
41
|
+
* fold over the issues accumulated — the same statement
|
|
42
|
+
* `buildReportModel` composes, but from a running total rather than a
|
|
43
|
+
* `filter().length`.
|
|
44
|
+
*/
|
|
45
|
+
function describeOmitted(count, floor) {
|
|
46
|
+
if (count === 0 || floor === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
return {
|
|
49
|
+
count,
|
|
50
|
+
floor,
|
|
51
|
+
statement: `${count} issue${count === 1 ? '' : 's'} below ${floor} severity ` +
|
|
52
|
+
`${count === 1 ? 'is' : 'are'} not listed individually in this document. ` +
|
|
53
|
+
`${count === 1 ? 'It remains' : 'They remain'} open and ` +
|
|
54
|
+
`${count === 1 ? 'is' : 'are'} included in every count above.`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Renders a report as JSON, from a header and a cursor over its issues,
|
|
59
|
+
* rather than from a fully-assembled {@link Snapshot}.
|
|
60
|
+
*
|
|
61
|
+
* **This is the machine path's whole reason to exist (B124).** `renderJson`
|
|
62
|
+
* holds every issue in one array before `JSON.stringify` walks it — measured
|
|
63
|
+
* at 239 MiB for 25,000 issues — so memory scales with the result and three
|
|
64
|
+
* concurrent exports exhaust a 512 MiB instance. This function instead
|
|
65
|
+
* yields the document a fragment at a time: everything that does not depend
|
|
66
|
+
* on having seen every issue is emitted immediately, the `issues` and
|
|
67
|
+
* `suppressed` arrays are written one element per row as the cursors
|
|
68
|
+
* produce them, and the handful of fields that need a full pass —
|
|
69
|
+
* `basis.includesManual`, `breached`, `omitted`, the retest counts — are
|
|
70
|
+
* accumulated in a fold whose state is a few counters, never the issues
|
|
71
|
+
* themselves, and written once both cursors are exhausted. Concatenating
|
|
72
|
+
* every yielded chunk produces one JSON document; nothing here buffers it
|
|
73
|
+
* as a string first.
|
|
74
|
+
*
|
|
75
|
+
* **`sections` grouped by severity and the sorted `resolved` list do not
|
|
76
|
+
* exist here.** Both require holding the whole array to sort; this function
|
|
77
|
+
* instead trusts that `issues` and `suppressed` already arrive in the order
|
|
78
|
+
* the document should show them; a cursor-based caller supplies that with a
|
|
79
|
+
* SQL `ORDER BY`, not with an in-memory sort. `includeResolved` and
|
|
80
|
+
* `severityFloor` still apply, filtering rows out of the stream rather than
|
|
81
|
+
* out of an array.
|
|
82
|
+
*
|
|
83
|
+
* @param header - The run, its baseline, and the target. Never scales with
|
|
84
|
+
* issue count.
|
|
85
|
+
* @param issues - Every issue in scope, most urgent first, in the order the
|
|
86
|
+
* document should list them.
|
|
87
|
+
* @param suppressed - The accepted-risk register, in the order the appendix
|
|
88
|
+
* should list it.
|
|
89
|
+
* @param options - Which report, attribution, and the render time.
|
|
90
|
+
* @returns Chunks of JSON text; concatenated, they are one document.
|
|
91
|
+
*/
|
|
92
|
+
export async function* renderJsonStream(header, issues, suppressed, options) {
|
|
93
|
+
const problem = brandingProblem(options.branding);
|
|
94
|
+
if (problem !== undefined)
|
|
95
|
+
throw new TypeError(problem);
|
|
96
|
+
// Rendering a retest of nothing is the over-claim `describeLimitations`'s
|
|
97
|
+
// caller in `model.ts` also refuses — the document's whole content is a
|
|
98
|
+
// comparison, and one run does not have one.
|
|
99
|
+
if (options.kind === 'retest' && header.baseline === undefined) {
|
|
100
|
+
throw new TypeError('a retest report needs a baseline: pass `baseline` on the header');
|
|
101
|
+
}
|
|
102
|
+
const attestor = resolveAttestor(options);
|
|
103
|
+
const floor = options.severityFloor;
|
|
104
|
+
const floorAt = floor === undefined ? SEVERITY_ORDER.length : SEVERITY_ORDER.indexOf(floor);
|
|
105
|
+
const state = newFoldState(header.baseline !== undefined);
|
|
106
|
+
yield '{';
|
|
107
|
+
yield field('reportVersion', 1);
|
|
108
|
+
yield ',' + field('kind', options.kind);
|
|
109
|
+
yield ',' + field('title', options.title ?? DEFAULT_TITLES[options.kind]);
|
|
110
|
+
yield ',' + field('generatedAt', options.now.toISOString());
|
|
111
|
+
if (options.preparedBy !== undefined)
|
|
112
|
+
yield ',' + field('preparedBy', options.preparedBy);
|
|
113
|
+
if (options.preparedFor !== undefined)
|
|
114
|
+
yield ',' + field('preparedFor', options.preparedFor);
|
|
115
|
+
yield ',' + field('attestor', attestor);
|
|
116
|
+
yield ',"target":' + JSON.stringify(header.target);
|
|
117
|
+
yield ',"run":' + JSON.stringify(header.run);
|
|
118
|
+
if (header.baseline !== undefined)
|
|
119
|
+
yield ',"baseline":' + JSON.stringify(header.baseline);
|
|
120
|
+
yield ',"issues":[';
|
|
121
|
+
let firstIssue = true;
|
|
122
|
+
for await (const entry of issues) {
|
|
123
|
+
if (entry.issue.origin === 'manual')
|
|
124
|
+
state.includesManual = true;
|
|
125
|
+
const status = entry.issue.status;
|
|
126
|
+
if (status === 'open' || status === 'regressed') {
|
|
127
|
+
if (entry.slaStatus === 'breached')
|
|
128
|
+
state.breached++;
|
|
129
|
+
}
|
|
130
|
+
let verdict;
|
|
131
|
+
if (state.retestCounts !== undefined) {
|
|
132
|
+
verdict = verdictFor(entry);
|
|
133
|
+
state.retestCounts[verdict]++;
|
|
134
|
+
}
|
|
135
|
+
// The floor decides what is *listed*, never what is *counted* — it was
|
|
136
|
+
// consulted above this line for exactly that reason, and it never
|
|
137
|
+
// applies to a resolved issue, which is filtered by `includeResolved`
|
|
138
|
+
// instead.
|
|
139
|
+
if (status === 'resolved') {
|
|
140
|
+
if (options.includeResolved === false)
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
else if (SEVERITY_ORDER.indexOf(entry.issue.effectiveSeverity) > floorAt) {
|
|
144
|
+
state.omittedBelowFloor++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const row = verdict === undefined ? entry : { ...entry, retestVerdict: verdict };
|
|
148
|
+
yield (firstIssue ? '' : ',') + JSON.stringify(row);
|
|
149
|
+
firstIssue = false;
|
|
150
|
+
}
|
|
151
|
+
yield ']';
|
|
152
|
+
yield ',"suppressed":[';
|
|
153
|
+
let firstSuppressed = true;
|
|
154
|
+
for await (const entry of suppressed) {
|
|
155
|
+
state.suppressedCount++;
|
|
156
|
+
yield (firstSuppressed ? '' : ',') + JSON.stringify(entry);
|
|
157
|
+
firstSuppressed = false;
|
|
158
|
+
}
|
|
159
|
+
yield ']';
|
|
160
|
+
const basis = describeBasis(header.run.kind, header.run.engines, state.includesManual);
|
|
161
|
+
const omitted = describeOmitted(state.omittedBelowFloor, floor);
|
|
162
|
+
const limitations = describeLimitations(header.run.coverage.paths, header.run.createdAt, basis, state.suppressedCount, omitted);
|
|
163
|
+
yield ',"basis":' + JSON.stringify(basis);
|
|
164
|
+
yield ',"limitations":' + JSON.stringify(limitations);
|
|
165
|
+
const outstanding = outstandingFrom(header.run);
|
|
166
|
+
yield ',"outstanding":' + JSON.stringify({ ...outstanding.by, total: outstanding.total });
|
|
167
|
+
yield ',' + field('exposureScore', header.run.exposureScore);
|
|
168
|
+
yield ',' + field('breached', state.breached);
|
|
169
|
+
if (omitted !== undefined)
|
|
170
|
+
yield ',"omitted":' + JSON.stringify(omitted);
|
|
171
|
+
if (state.retestCounts !== undefined) {
|
|
172
|
+
const checkable = state.retestCounts.fixed + state.retestCounts.still_present + state.retestCounts.returned;
|
|
173
|
+
yield ',"retest":' +
|
|
174
|
+
JSON.stringify({
|
|
175
|
+
counts: state.retestCounts,
|
|
176
|
+
fixRate: checkable === 0 ? null : state.retestCounts.fixed / checkable,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
yield '}';
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Renders a report as Markdown, from a header and a cursor over its issues,
|
|
183
|
+
* rather than from a fully-assembled {@link Snapshot}. The streaming sibling
|
|
184
|
+
* of {@link renderJsonStream} — see it for the memory argument, which applies
|
|
185
|
+
* here identically: `renderMarkdown` holds every issue in one array before
|
|
186
|
+
* formatting any of it.
|
|
187
|
+
*
|
|
188
|
+
* **The document is reordered relative to `renderMarkdown`, and that is
|
|
189
|
+
* forced rather than stylistic.** The array-based renderer opens with "Basis
|
|
190
|
+
* of testing" and a "What changed" line naming how many issues are past
|
|
191
|
+
* their remediation deadline — both need to know something about *every*
|
|
192
|
+
* issue (`basis.includesManual`, `breached`) before the first line can be
|
|
193
|
+
* printed, which a forward-only stream cannot offer without buffering
|
|
194
|
+
* everything first. So this function prints what it already knows up front
|
|
195
|
+
* (title, scope, the New/Still open/Regressed/Resolved/Suppressed table,
|
|
196
|
+
* which all read straight off `RunSummary`) and moves everything that needs
|
|
197
|
+
* a full pass — the basis statement, the remediation-deadline count, what a
|
|
198
|
+
* severity floor omitted, the retest verdict counts — into a closing
|
|
199
|
+
* `## Summary` section, written once both cursors are exhausted.
|
|
200
|
+
*
|
|
201
|
+
* **`sections` grouped by severity do not exist here**, for the reason given
|
|
202
|
+
* in {@link renderJsonStream}: `issues` and `suppressed` are trusted to
|
|
203
|
+
* already arrive in the order the document should show them. For a `pen`
|
|
204
|
+
* report that means a severity sub-heading appears whenever the incoming
|
|
205
|
+
* severity changes, without the issue count that heading carries in
|
|
206
|
+
* `renderMarkdown` — the count is not known until the section ends. A
|
|
207
|
+
* `retest` report's per-issue table has no severity floor applied to it,
|
|
208
|
+
* matching `renderMarkdown`: the whole point of that table is showing what
|
|
209
|
+
* happened to every issue carried into the retest, not a curated subset.
|
|
210
|
+
*
|
|
211
|
+
* **`attest` buffers its suppressed list; nothing else buffers issues.** An
|
|
212
|
+
* Attestation Letter states counts and names nobody's individual issue, so
|
|
213
|
+
* it folds the whole `issues` cursor for those counts without printing a
|
|
214
|
+
* row — but it also prints the suppressed appendix *after* that letter body,
|
|
215
|
+
* by which point the `suppressed` cursor would already be exhausted. Holding
|
|
216
|
+
* that one list (the accepted-risk register, ordinarily a minority of the
|
|
217
|
+
* total) is the one deliberate exception to "never hold more than a row".
|
|
218
|
+
*
|
|
219
|
+
* @param header - The run, its baseline, and the target. Never scales with
|
|
220
|
+
* issue count.
|
|
221
|
+
* @param issues - Every issue in scope, most urgent first: open/regressed
|
|
222
|
+
* before resolved, each group by severity then most-recently-seen first —
|
|
223
|
+
* the order the document lists them in.
|
|
224
|
+
* @param suppressed - The accepted-risk register, in the order the appendix
|
|
225
|
+
* should list it.
|
|
226
|
+
* @param options - Which report, attribution, and the render time.
|
|
227
|
+
* @returns Chunks of Markdown text; concatenated, they are one document.
|
|
228
|
+
*/
|
|
229
|
+
export async function* renderMarkdownStream(header, issues, suppressed, options) {
|
|
230
|
+
const problem = brandingProblem(options.branding);
|
|
231
|
+
if (problem !== undefined)
|
|
232
|
+
throw new TypeError(problem);
|
|
233
|
+
if (options.kind === 'retest' && header.baseline === undefined) {
|
|
234
|
+
throw new TypeError('a retest report needs a baseline: pass `baseline` on the header');
|
|
235
|
+
}
|
|
236
|
+
const attestor = resolveAttestor(options);
|
|
237
|
+
const { target } = header;
|
|
238
|
+
const floor = options.severityFloor;
|
|
239
|
+
const floorAt = floor === undefined ? SEVERITY_ORDER.length : SEVERITY_ORDER.indexOf(floor);
|
|
240
|
+
const state = newFoldState(header.baseline !== undefined);
|
|
241
|
+
const outstanding = outstandingFrom(header.run);
|
|
242
|
+
const preamble = [];
|
|
243
|
+
if (options.branding?.watermark !== undefined) {
|
|
244
|
+
preamble.push(`**${options.branding.watermark}**`, '');
|
|
245
|
+
}
|
|
246
|
+
preamble.push(`# ${options.title ?? DEFAULT_TITLES[options.kind]}`, '', `**${target.name}** — ${target.url}`);
|
|
247
|
+
if (options.coverPage === true) {
|
|
248
|
+
preamble.push('', ...[
|
|
249
|
+
attestor,
|
|
250
|
+
...(options.branding?.companyDetails ?? []),
|
|
251
|
+
...(options.preparedFor === undefined ? [] : [`Prepared for ${options.preparedFor}`]),
|
|
252
|
+
].map((line, i) => (i === 0 ? line : `_${line}_`)));
|
|
253
|
+
}
|
|
254
|
+
preamble.push('', '| | |', '| --- | --- |', `| Generated | ${formatDate(options.now)} |`);
|
|
255
|
+
preamble.push(`| Run | ${header.run.runId} (${header.run.trigger}) |`);
|
|
256
|
+
if (options.preparedFor !== undefined)
|
|
257
|
+
preamble.push(`| Prepared for | ${cell(options.preparedFor)} |`);
|
|
258
|
+
if (options.preparedBy !== undefined)
|
|
259
|
+
preamble.push(`| Prepared by | ${cell(options.preparedBy)} |`);
|
|
260
|
+
preamble.push('');
|
|
261
|
+
yield preamble.join('\n') + '\n';
|
|
262
|
+
if (options.kind === 'attest') {
|
|
263
|
+
// No per-row content: an Attestation Letter names counts, not issues. The
|
|
264
|
+
// whole `issues` cursor is folded for `state` and never printed; the
|
|
265
|
+
// suppressed one is folded *and* buffered, since it is printed after the
|
|
266
|
+
// letter body, by which point it would otherwise be exhausted.
|
|
267
|
+
let totalFound = 0;
|
|
268
|
+
for await (const entry of issues) {
|
|
269
|
+
totalFound++;
|
|
270
|
+
if (entry.issue.origin === 'manual')
|
|
271
|
+
state.includesManual = true;
|
|
272
|
+
// An Attestation Letter never lists issues individually regardless of
|
|
273
|
+
// the floor, but `buildReportModel` still folds `omitted` into
|
|
274
|
+
// `limitations` whenever one is set, kind notwithstanding — matched
|
|
275
|
+
// here rather than silently diverging.
|
|
276
|
+
if (entry.issue.status !== 'resolved' &&
|
|
277
|
+
SEVERITY_ORDER.indexOf(entry.issue.effectiveSeverity) > floorAt) {
|
|
278
|
+
state.omittedBelowFloor++;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const suppressedRows = [];
|
|
282
|
+
for await (const entry of suppressed) {
|
|
283
|
+
totalFound++;
|
|
284
|
+
state.suppressedCount++;
|
|
285
|
+
suppressedRows.push(entry);
|
|
286
|
+
}
|
|
287
|
+
const basis = describeBasis(header.run.kind, header.run.engines, state.includesManual);
|
|
288
|
+
const letter = [
|
|
289
|
+
'## Statement',
|
|
290
|
+
'',
|
|
291
|
+
`${attestor} carried out security testing of **${target.name}**`,
|
|
292
|
+
`(${target.url}) on ${formatDate(header.run.createdAt)}.`,
|
|
293
|
+
'',
|
|
294
|
+
basis.statement,
|
|
295
|
+
'',
|
|
296
|
+
`The testing identified ${totalFound} issue${totalFound === 1 ? '' : 's'} in total, of which`,
|
|
297
|
+
`${outstanding.total} ${outstanding.total === 1 ? 'remains' : 'remain'} outstanding at the date of this letter:`,
|
|
298
|
+
'',
|
|
299
|
+
'| Severity | Outstanding |',
|
|
300
|
+
'| --- | --- |',
|
|
301
|
+
...SEVERITY_ORDER.map((sev) => `| ${sev} | ${outstanding.by[sev]} |`),
|
|
302
|
+
'',
|
|
303
|
+
'## What this letter does not establish',
|
|
304
|
+
'',
|
|
305
|
+
...describeLimitations(header.run.coverage.paths, header.run.createdAt, basis, state.suppressedCount, describeOmitted(state.omittedBelowFloor, floor)).map((l) => `- ${l}`),
|
|
306
|
+
'',
|
|
307
|
+
'## Scope tested',
|
|
308
|
+
'',
|
|
309
|
+
...header.run.coverage.paths.map((p) => `- \`${cell(p)}\``),
|
|
310
|
+
'',
|
|
311
|
+
'## Suppressed findings',
|
|
312
|
+
'',
|
|
313
|
+
];
|
|
314
|
+
yield letter.join('\n') + '\n';
|
|
315
|
+
yield suppressedTable(suppressedRows);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
yield [
|
|
319
|
+
'## Scope',
|
|
320
|
+
'',
|
|
321
|
+
`This report covers only what the run exercised: ${header.run.coverage.paths.map((p) => `\`${cell(p)}\``).join(', ')}.`,
|
|
322
|
+
'Anything outside that was not tested and is not described here.',
|
|
323
|
+
'',
|
|
324
|
+
].join('\n') + '\n';
|
|
325
|
+
const changeRows = SEVERITY_ORDER.map((severity) => {
|
|
326
|
+
const cells = [
|
|
327
|
+
severity,
|
|
328
|
+
header.run.new[severity],
|
|
329
|
+
header.run.stillOpen[severity],
|
|
330
|
+
header.run.regressed[severity],
|
|
331
|
+
header.run.resolved[severity],
|
|
332
|
+
header.run.ignored[severity],
|
|
333
|
+
];
|
|
334
|
+
return `| ${cells.join(' | ')} |`;
|
|
335
|
+
});
|
|
336
|
+
yield [
|
|
337
|
+
'## What changed',
|
|
338
|
+
'',
|
|
339
|
+
header.baseline === undefined
|
|
340
|
+
? '_First run against this target, so every issue is new._'
|
|
341
|
+
: `_Compared with the run of ${formatDate(header.baseline.createdAt)}._`,
|
|
342
|
+
'',
|
|
343
|
+
'| Severity | New | Still open | Regressed | Resolved | Suppressed |',
|
|
344
|
+
'| --- | --- | --- | --- | --- | --- |',
|
|
345
|
+
...changeRows,
|
|
346
|
+
'',
|
|
347
|
+
].join('\n') + '\n';
|
|
348
|
+
const notable = [];
|
|
349
|
+
let openedFindings = false;
|
|
350
|
+
let lastSeverity;
|
|
351
|
+
let anyListed = false;
|
|
352
|
+
const resolvedRows = [];
|
|
353
|
+
for await (const entry of issues) {
|
|
354
|
+
if (entry.issue.origin === 'manual')
|
|
355
|
+
state.includesManual = true;
|
|
356
|
+
const status = entry.issue.status;
|
|
357
|
+
if (status === 'open' || status === 'regressed') {
|
|
358
|
+
if (entry.slaStatus === 'breached')
|
|
359
|
+
state.breached++;
|
|
360
|
+
}
|
|
361
|
+
let verdict;
|
|
362
|
+
if (state.retestCounts !== undefined) {
|
|
363
|
+
verdict = verdictFor(entry);
|
|
364
|
+
state.retestCounts[verdict]++;
|
|
365
|
+
}
|
|
366
|
+
if (options.kind === 'retest') {
|
|
367
|
+
// No severity floor and no `includeResolved` filter: every issue
|
|
368
|
+
// carried into the retest gets a row, matching `renderMarkdown`.
|
|
369
|
+
if (!openedFindings) {
|
|
370
|
+
yield [
|
|
371
|
+
'## Issue by issue',
|
|
372
|
+
'',
|
|
373
|
+
'| Issue | Severity | Verdict | Location | Open for |',
|
|
374
|
+
'| --- | --- | --- | --- | --- |',
|
|
375
|
+
].join('\n') + '\n';
|
|
376
|
+
openedFindings = true;
|
|
377
|
+
}
|
|
378
|
+
const { issue } = entry;
|
|
379
|
+
const location = issue.parameter === undefined ? issue.location : `${issue.location} (${issue.parameter})`;
|
|
380
|
+
yield `| ${cell(issue.title)} | ${issue.effectiveSeverity} | ${VERDICT_LABELS[verdict]} | \`${cell(location)}\` | ${entry.daysOpen} days |\n`;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (status === 'resolved') {
|
|
384
|
+
if (options.includeResolved === false)
|
|
385
|
+
continue;
|
|
386
|
+
resolvedRows.push(`| ${cell(entry.issue.title)} | ${entry.issue.effectiveSeverity} | \`${cell(entry.issue.location)}\` | ${entry.daysOpen} days |`);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (SEVERITY_ORDER.indexOf(entry.issue.effectiveSeverity) > floorAt) {
|
|
390
|
+
state.omittedBelowFloor++;
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
anyListed = true;
|
|
394
|
+
if (options.kind === 'exec') {
|
|
395
|
+
if (['critical', 'high'].includes(entry.issue.effectiveSeverity) && notable.length < 10) {
|
|
396
|
+
notable.push(entry);
|
|
397
|
+
}
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (options.kind === 'pen') {
|
|
401
|
+
if (!openedFindings) {
|
|
402
|
+
yield '## Findings\n\n';
|
|
403
|
+
openedFindings = true;
|
|
404
|
+
}
|
|
405
|
+
if (entry.issue.effectiveSeverity !== lastSeverity) {
|
|
406
|
+
lastSeverity = entry.issue.effectiveSeverity;
|
|
407
|
+
yield `### ${lastSeverity}\n\n`;
|
|
408
|
+
}
|
|
409
|
+
yield issueDetail(entry, options.evidenceVerbosity ?? 'summary').join('\n') + '\n';
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
// `vap`, the tabulated presentation — the default for any kind not
|
|
413
|
+
// handled above.
|
|
414
|
+
if (!openedFindings) {
|
|
415
|
+
yield [
|
|
416
|
+
'## Findings',
|
|
417
|
+
'',
|
|
418
|
+
'| Issue | Severity | Location | Change | Days open | Remediation |',
|
|
419
|
+
'| --- | --- | --- | --- | --- | --- |',
|
|
420
|
+
].join('\n') + '\n';
|
|
421
|
+
openedFindings = true;
|
|
422
|
+
}
|
|
423
|
+
yield issueRow(entry) + '\n';
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (options.kind === 'exec') {
|
|
427
|
+
yield executiveSummary(outstanding, state, notable).join('\n') + '\n';
|
|
428
|
+
}
|
|
429
|
+
else if (options.kind !== 'retest') {
|
|
430
|
+
if (!anyListed) {
|
|
431
|
+
yield [
|
|
432
|
+
'## Findings',
|
|
433
|
+
'',
|
|
434
|
+
floor === undefined
|
|
435
|
+
? '_No issues were found._'
|
|
436
|
+
: '_No issues at or above the reporting threshold were found._',
|
|
437
|
+
'',
|
|
438
|
+
].join('\n') + '\n';
|
|
439
|
+
}
|
|
440
|
+
if (resolvedRows.length > 0) {
|
|
441
|
+
const n = resolvedRows.length;
|
|
442
|
+
yield [
|
|
443
|
+
'## Resolved since the last run',
|
|
444
|
+
'',
|
|
445
|
+
`${n} issue${n === 1 ? ' was' : 's were'} no longer detected by a run that covered ${n === 1 ? 'it' : 'them'}.`,
|
|
446
|
+
'',
|
|
447
|
+
'| Issue | Severity | Location | Open for |',
|
|
448
|
+
'| --- | --- | --- | --- |',
|
|
449
|
+
...resolvedRows,
|
|
450
|
+
'',
|
|
451
|
+
].join('\n') + '\n';
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
yield '## Suppressed findings\n\n';
|
|
455
|
+
yield suppressedTable(await drain(suppressed, state));
|
|
456
|
+
const basis = describeBasis(header.run.kind, header.run.engines, state.includesManual);
|
|
457
|
+
const summary = ['## Summary', '', '## Basis of testing', '', basis.statement, ''];
|
|
458
|
+
if (state.breached > 0) {
|
|
459
|
+
summary.push(`**${state.breached} issue${state.breached === 1 ? '' : 's'} past ${state.breached === 1 ? 'its' : 'their'} remediation deadline.**`, '');
|
|
460
|
+
}
|
|
461
|
+
const omitted = describeOmitted(state.omittedBelowFloor, floor);
|
|
462
|
+
if (omitted !== undefined)
|
|
463
|
+
summary.push(omitted.statement, '');
|
|
464
|
+
if (options.kind === 'retest' && state.retestCounts !== undefined) {
|
|
465
|
+
const counts = state.retestCounts;
|
|
466
|
+
const carried = counts.fixed + counts.still_present + counts.returned + counts.not_retested;
|
|
467
|
+
const checkable = counts.fixed + counts.still_present + counts.returned;
|
|
468
|
+
const fixRate = checkable === 0 ? null : counts.fixed / checkable;
|
|
469
|
+
summary.push('## Retest verdict', '', carried === 0
|
|
470
|
+
? 'No issues were carried into this retest.'
|
|
471
|
+
: `Of the ${carried} issue${carried === 1 ? '' : 's'} carried into this retest, ` +
|
|
472
|
+
`**${counts.fixed} ${counts.fixed === 1 ? 'is' : 'are'} confirmed fixed**, ` +
|
|
473
|
+
`${counts.still_present} ${counts.still_present === 1 ? 'is' : 'are'} still present, ` +
|
|
474
|
+
`${counts.returned} returned, and ${counts.not_retested} could not be retested.` +
|
|
475
|
+
(counts.new === 0
|
|
476
|
+
? ''
|
|
477
|
+
: ` A further ${counts.new} ${counts.new === 1 ? 'issue was' : 'issues were'} found for the first time by this run.`), '');
|
|
478
|
+
if (fixRate !== null) {
|
|
479
|
+
summary.push(`${Math.round(fixRate * 100)}% of the issues this run could check are fixed.`, '');
|
|
480
|
+
}
|
|
481
|
+
summary.push('| Verdict | Issues |', '| --- | --- |', ...Object.keys(VERDICT_LABELS).map((v) => `| ${VERDICT_LABELS[v]} | ${counts[v]} |`), '');
|
|
482
|
+
// **Its own heading, not just a sentence.** The array renderer gives this
|
|
483
|
+
// a `## Not retested` section because it is the part of a retest that
|
|
484
|
+
// stops a coverage gap reading as remediation; the first draft of this
|
|
485
|
+
// one kept the sentence and dropped the heading, which a test comparing
|
|
486
|
+
// the two formats caught.
|
|
487
|
+
if (counts.not_retested > 0) {
|
|
488
|
+
summary.push('## Not retested', '', `${counts.not_retested} issue${counts.not_retested === 1 ? '' : 's'} could not be retested by this run:`, 'the run did not cover where they were found, or has not missed them often enough to', 'call them resolved. **They are not fixed.** An issue nobody looked at is neither fixed', 'nor unfixed, and it is excluded from the percentage above rather than counted either way.', '');
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
yield summary.join('\n') + '\n';
|
|
492
|
+
}
|
|
493
|
+
/** Reads the rest of an `AsyncIterable<SuppressedIssue>`, folding as it goes. */
|
|
494
|
+
async function drain(suppressed, state) {
|
|
495
|
+
const rows = [];
|
|
496
|
+
for await (const entry of suppressed) {
|
|
497
|
+
state.suppressedCount++;
|
|
498
|
+
rows.push(entry);
|
|
499
|
+
}
|
|
500
|
+
return rows;
|
|
501
|
+
}
|
|
502
|
+
/** The suppressed-findings table. On by default, never optional (invariant 7). */
|
|
503
|
+
function suppressedTable(rows) {
|
|
504
|
+
if (rows.length === 0)
|
|
505
|
+
return '_None._\n';
|
|
506
|
+
return ([
|
|
507
|
+
'Accepted, out of scope or dismissed. Listed because an accepted risk that',
|
|
508
|
+
'nobody can see is not an accepted risk.',
|
|
509
|
+
'',
|
|
510
|
+
'| Issue | Severity when suppressed | Reason | By | Expires | Justification |',
|
|
511
|
+
'| --- | --- | --- | --- | --- | --- |',
|
|
512
|
+
...rows.map((s) => `| ${cell(s.issue.title)} | ${s.severityAtIgnore} | ${s.reason.replace('_', ' ')} | ${cell(s.ignoredBy)} | ${s.expiresAt === undefined ? 'never' : formatDate(s.expiresAt)} | ${cell(s.comment)} |`),
|
|
513
|
+
].join('\n') + '\n');
|
|
514
|
+
}
|
|
515
|
+
/** The Executive Summary body, once the fold and the top-10 buffer are complete. */
|
|
516
|
+
function executiveSummary(outstanding, state, notable) {
|
|
517
|
+
const headline = outstanding.total === 0
|
|
518
|
+
? 'Nothing is currently outstanding on this target.'
|
|
519
|
+
: `${outstanding.total} issue${outstanding.total === 1 ? '' : 's'} ${outstanding.total === 1 ? 'is' : 'are'} currently outstanding, of which ${outstanding.by.critical + outstanding.by.high} ` +
|
|
520
|
+
`${outstanding.by.critical + outstanding.by.high === 1 ? 'is' : 'are'} critical or high.`;
|
|
521
|
+
const lines = ['## Where things stand', '', headline, ''];
|
|
522
|
+
if (state.breached > 0) {
|
|
523
|
+
lines.push(`**${state.breached} ${state.breached === 1 ? 'is' : 'are'} past the remediation deadline** ` +
|
|
524
|
+
'set by the severity policy.', '');
|
|
525
|
+
}
|
|
526
|
+
lines.push('## Outstanding by severity', '', '| Severity | Outstanding |', '| --- | --- |', ...SEVERITY_ORDER.filter((sev) => outstanding.by[sev] > 0).map((sev) => `| ${sev} | ${outstanding.by[sev]} |`), '');
|
|
527
|
+
if (outstanding.total === 0)
|
|
528
|
+
lines.push('_Nothing outstanding._', '');
|
|
529
|
+
if (notable.length > 0) {
|
|
530
|
+
lines.push('## What needs attention first', '', ...notable.map((entry) => `- **${entry.issue.effectiveSeverity}** — ${entry.issue.title} (open ${entry.daysOpen} days)`), '', '_Full detail, including evidence, is in the accompanying technical report._', '');
|
|
531
|
+
}
|
|
532
|
+
return lines;
|
|
533
|
+
}
|
|
534
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/report/stream.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@secureport/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Shared domain model for Secureport. Imported by the hosted API the same way a third party would.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"security",
|
|
@@ -37,7 +37,9 @@
|
|
|
37
37
|
"LICENSE"
|
|
38
38
|
],
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@types/node": "^22.20.
|
|
40
|
+
"@types/node": "^22.20.2",
|
|
41
|
+
"ajv": "^8.20.0",
|
|
42
|
+
"ajv-draft-04": "^1.0.0",
|
|
41
43
|
"typescript": "^5.7.2"
|
|
42
44
|
},
|
|
43
45
|
"license": "MIT",
|
package/src/index.ts
CHANGED
|
@@ -53,6 +53,9 @@ export { importGeneric } from './import/generic.js';
|
|
|
53
53
|
export type { BuildSnapshotInput } from './snapshot-builder.js';
|
|
54
54
|
export { buildSnapshot, parseSnapshot } from './snapshot-builder.js';
|
|
55
55
|
export type {
|
|
56
|
+
Branding,
|
|
57
|
+
EvidenceVerbosity,
|
|
58
|
+
OmittedIssues,
|
|
56
59
|
ReportKind,
|
|
57
60
|
ReportModel,
|
|
58
61
|
ReportOptions,
|
|
@@ -62,12 +65,18 @@ export type {
|
|
|
62
65
|
RetestVerdict,
|
|
63
66
|
TestingBasis,
|
|
64
67
|
} from './report/model.js';
|
|
65
|
-
export { buildReportModel } from './report/model.js';
|
|
68
|
+
export { brandingProblem, buildReportModel, VERDICT_LABELS } from './report/model.js';
|
|
66
69
|
export { renderMarkdown } from './report/markdown.js';
|
|
67
70
|
export { renderHtml } from './report/html.js';
|
|
71
|
+
export type { ReportHeader } from './report/stream.js';
|
|
72
|
+
export { renderJsonStream, renderMarkdownStream } from './report/stream.js';
|
|
73
|
+
export type { SarifOptions } from './report/sarif.js';
|
|
74
|
+
export { renderSarifStream } from './report/sarif.js';
|
|
75
|
+
export type { CsvOptions } from './report/csv.js';
|
|
76
|
+
export { renderCsvStream } from './report/csv.js';
|
|
68
77
|
export type { JsonReport } from './report/json.js';
|
|
69
78
|
export { renderJson } from './report/json.js';
|
|
70
|
-
export type { ReconcileInput, ReconcileResult } from './reconcile.js';
|
|
79
|
+
export type { ReconcileInput, ReconcileResult, SuppressionDecision } from './reconcile.js';
|
|
71
80
|
export { reconcile } from './reconcile.js';
|
|
72
81
|
export type {
|
|
73
82
|
Coverage,
|