@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.
Files changed (53) hide show
  1. package/dist/index.d.ts +9 -3
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +4 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/issue.d.ts +1 -1
  6. package/dist/issue.d.ts.map +1 -1
  7. package/dist/reconcile.d.ts +46 -1
  8. package/dist/reconcile.d.ts.map +1 -1
  9. package/dist/reconcile.js +34 -1
  10. package/dist/reconcile.js.map +1 -1
  11. package/dist/report/anchors.d.ts +39 -0
  12. package/dist/report/anchors.d.ts.map +1 -0
  13. package/dist/report/anchors.js +73 -0
  14. package/dist/report/anchors.js.map +1 -0
  15. package/dist/report/csv.d.ts +41 -0
  16. package/dist/report/csv.d.ts.map +1 -0
  17. package/dist/report/csv.js +152 -0
  18. package/dist/report/csv.js.map +1 -0
  19. package/dist/report/html.d.ts.map +1 -1
  20. package/dist/report/html.js +126 -22
  21. package/dist/report/html.js.map +1 -1
  22. package/dist/report/json.d.ts +15 -0
  23. package/dist/report/json.d.ts.map +1 -1
  24. package/dist/report/json.js +1 -0
  25. package/dist/report/json.js.map +1 -1
  26. package/dist/report/markdown.d.ts +24 -2
  27. package/dist/report/markdown.d.ts.map +1 -1
  28. package/dist/report/markdown.js +133 -31
  29. package/dist/report/markdown.js.map +1 -1
  30. package/dist/report/model.d.ts +288 -2
  31. package/dist/report/model.d.ts.map +1 -1
  32. package/dist/report/model.js +153 -21
  33. package/dist/report/model.js.map +1 -1
  34. package/dist/report/sarif.d.ts +60 -0
  35. package/dist/report/sarif.d.ts.map +1 -0
  36. package/dist/report/sarif.js +125 -0
  37. package/dist/report/sarif.js.map +1 -0
  38. package/dist/report/stream.d.ts +108 -0
  39. package/dist/report/stream.d.ts.map +1 -0
  40. package/dist/report/stream.js +534 -0
  41. package/dist/report/stream.js.map +1 -0
  42. package/package.json +4 -2
  43. package/src/index.ts +11 -2
  44. package/src/issue.ts +1 -0
  45. package/src/reconcile.ts +86 -2
  46. package/src/report/anchors.ts +96 -0
  47. package/src/report/csv.ts +180 -0
  48. package/src/report/html.ts +146 -26
  49. package/src/report/json.ts +17 -0
  50. package/src/report/markdown.ts +146 -30
  51. package/src/report/model.ts +433 -21
  52. package/src/report/sarif.ts +166 -0
  53. 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":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EACL,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,UAAU,EACV,cAAc,GAIf,MAAM,YAAY,CAAC;AAiCpB,SAAS,YAAY,CAAC,WAAoB;IACxC,OAAO;QACL,cAAc,EAAE,KAAK;QACrB,QAAQ,EAAE,CAAC;QACX,iBAAiB,EAAE,CAAC;QACpB,eAAe,EAAE,CAAC;QAClB,GAAG,CAAC,WAAW;YACb,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;YACxF,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,SAAS,KAAK,CAAC,GAAW,EAAE,KAAc;IACxC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,eAAe,CAAC,GAAe;IACtC,MAAM,EAAE,GAA6B;QACnC,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,QAAQ;QAC5E,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI;QAC5D,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM;QACpE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG;QACxD,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,QAAQ;KAC7E,CAAC;IACF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,KAAa,EAAE,KAA2B;IACjE,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzD,OAAO;QACL,KAAK;QACL,KAAK;QACL,SAAS,EACP,GAAG,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,UAAU,KAAK,YAAY;YAClE,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,6CAA6C;YAC1E,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,YAAY;YACzD,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,iCAAiC;KACjE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,gBAAgB,CACrC,MAAoB,EACpB,MAAoC,EACpC,UAA0C,EAC1C,OAAsB;IAEtB,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,OAAO,KAAK,SAAS;QAAE,MAAM,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;IAExD,0EAA0E;IAC1E,wEAAwE;IACxE,6CAA6C;IAC7C,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/D,MAAM,IAAI,SAAS,CAAC,iEAAiE,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5F,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC;IAE1D,MAAM,GAAG,CAAC;IACV,MAAM,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1E,MAAM,GAAG,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS;QAAE,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1F,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS;QAAE,MAAM,GAAG,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7F,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACxC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;QAAE,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE1F,MAAM,aAAa,CAAC;IACpB,IAAI,UAAU,GAAG,IAAI,CAAC;IACtB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ;YAAE,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;QAEjE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC;QAClC,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAChD,IAAI,KAAK,CAAC,SAAS,KAAK,UAAU;gBAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QACvD,CAAC;QAED,IAAI,OAAkC,CAAC;QACvC,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACrC,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;YAC5B,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,CAAC;QAED,uEAAuE;QACvE,kEAAkE;QAClE,sEAAsE;QACtE,WAAW;QACX,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;YAC1B,IAAI,OAAO,CAAC,eAAe,KAAK,KAAK;gBAAE,SAAS;QAClD,CAAC;aAAM,IAAI,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,OAAO,EAAE,CAAC;YAC3E,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC1B,SAAS;QACX,CAAC;QAED,MAAM,GAAG,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;QACjF,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACpD,UAAU,GAAG,KAAK,CAAC;IACrB,CAAC;IACD,MAAM,GAAG,CAAC;IAEV,MAAM,iBAAiB,CAAC;IACxB,IAAI,eAAe,GAAG,IAAI,CAAC;IAC3B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QACrC,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC3D,eAAe,GAAG,KAAK,CAAC;IAC1B,CAAC;IACD,MAAM,GAAG,CAAC;IAEV,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IACvF,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IAChE,MAAM,WAAW,GAAG,mBAAmB,CACrC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EACzB,MAAM,CAAC,GAAG,CAAC,SAAS,EACpB,KAAK,EACL,KAAK,CAAC,eAAe,EACrB,OAAO,CACR,CAAC;IAEF,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAEtD,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1F,MAAM,GAAG,GAAG,KAAK,CAAC,eAAe,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9C,IAAI,OAAO,KAAK,SAAS;QAAE,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAEzE,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,SAAS,GACb,KAAK,CAAC,YAAY,CAAC,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC;QAC5F,MAAM,YAAY;YAChB,IAAI,CAAC,SAAS,CAAC;gBACb,MAAM,EAAE,KAAK,CAAC,YAAY;gBAC1B,OAAO,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,GAAG,SAAS;aACvE,CAAC,CAAC;IACP,CAAC;IAED,MAAM,GAAG,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,oBAAoB,CACzC,MAAoB,EACpB,MAAoC,EACpC,UAA0C,EAC1C,OAAsB;IAEtB,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,OAAO,KAAK,SAAS;QAAE,MAAM,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/D,MAAM,IAAI,SAAS,CAAC,iEAAiE,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5F,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAEhD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,OAAO,CAAC,QAAQ,EAAE,SAAS,KAAK,SAAS,EAAE,CAAC;QAC9C,QAAQ,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,QAAQ,CAAC,IAAI,CACX,KAAK,OAAO,CAAC,KAAK,IAAI,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EACpD,EAAE,EACF,KAAK,MAAM,CAAC,IAAI,QAAQ,MAAM,CAAC,GAAG,EAAE,CACrC,CAAC;IACF,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QAC/B,QAAQ,CAAC,IAAI,CACX,EAAE,EACF,GAAG;YACD,QAAQ;YACR,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,IAAI,EAAE,CAAC;YAC3C,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,gBAAgB,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;SACtF,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CACnD,CAAC;IACJ,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,iBAAiB,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1F,QAAQ,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,GAAG,CAAC,KAAK,KAAK,MAAM,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC;IACvE,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS;QACnC,QAAQ,CAAC,IAAI,CAAC,oBAAoB,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACnE,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS;QAClC,QAAQ,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClB,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEjC,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,0EAA0E;QAC1E,qEAAqE;QACrE,yEAAyE;QACzE,+DAA+D;QAC/D,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,UAAU,EAAE,CAAC;YACb,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ;gBAAE,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;YACjE,sEAAsE;YACtE,+DAA+D;YAC/D,oEAAoE;YACpE,uCAAuC;YACvC,IACE,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,UAAU;gBACjC,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,OAAO,EAC/D,CAAC;gBACD,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,CAAC;QACH,CAAC;QACD,MAAM,cAAc,GAAsB,EAAE,CAAC;QAC7C,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YACrC,UAAU,EAAE,CAAC;YACb,KAAK,CAAC,eAAe,EAAE,CAAC;YACxB,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;QAEvF,MAAM,MAAM,GAAa;YACvB,cAAc;YACd,EAAE;YACF,GAAG,QAAQ,sCAAsC,MAAM,CAAC,IAAI,IAAI;YAChE,IAAI,MAAM,CAAC,GAAG,QAAQ,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG;YACzD,EAAE;YACF,KAAK,CAAC,SAAS;YACf,EAAE;YACF,0BAA0B,UAAU,SAAS,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,qBAAqB;YAC7F,GAAG,WAAW,CAAC,KAAK,IAAI,WAAW,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,0CAA0C;YAChH,EAAE;YACF,4BAA4B;YAC5B,eAAe;YACf,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;YACrE,EAAE;YACF,wCAAwC;YACxC,EAAE;YACF,GAAG,mBAAmB,CACpB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EACzB,MAAM,CAAC,GAAG,CAAC,SAAS,EACpB,KAAK,EACL,KAAK,CAAC,eAAe,EACrB,eAAe,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAChD,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YACtB,EAAE;YACF,iBAAiB;YACjB,EAAE;YACF,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YAC3D,EAAE;YACF,wBAAwB;YACxB,EAAE;SACH,CAAC;QACF,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAC/B,MAAM,eAAe,CAAC,cAAc,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,MAAM;QACJ,UAAU;QACV,EAAE;QACF,mDAAmD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QACvH,iEAAiE;QACjE,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEpB,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;QACjD,MAAM,KAAK,GAAG;YACZ,QAAQ;YACR,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;YAC9B,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;YAC9B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC;SAC7B,CAAC;QACF,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACpC,CAAC,CAAC,CAAC;IACH,MAAM;QACJ,iBAAiB;QACjB,EAAE;QACF,MAAM,CAAC,QAAQ,KAAK,SAAS;YAC3B,CAAC,CAAC,yDAAyD;YAC3D,CAAC,CAAC,6BAA6B,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI;QAC1E,EAAE;QACF,qEAAqE;QACrE,uCAAuC;QACvC,GAAG,UAAU;QACb,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEpB,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,IAAI,YAAkC,CAAC;IACvC,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ;YAAE,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;QACjE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC;QAClC,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAChD,IAAI,KAAK,CAAC,SAAS,KAAK,UAAU;gBAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QACvD,CAAC;QAED,IAAI,OAAkC,CAAC;QACvC,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACrC,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;YAC5B,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,iEAAiE;YACjE,iEAAiE;YACjE,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,MAAM;oBACJ,mBAAmB;oBACnB,EAAE;oBACF,sDAAsD;oBACtD,iCAAiC;iBAClC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;gBACpB,cAAc,GAAG,IAAI,CAAC;YACxB,CAAC;YACD,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;YACxB,MAAM,QAAQ,GACZ,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC;YAC5F,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,iBAAiB,MAAM,cAAc,CAAC,OAAwB,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,CAAC,QAAQ,WAAW,CAAC;YAC/J,SAAS;QACX,CAAC;QAED,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;YAC1B,IAAI,OAAO,CAAC,eAAe,KAAK,KAAK;gBAAE,SAAS;YAChD,YAAY,CAAC,IAAI,CACf,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,iBAAiB,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,KAAK,CAAC,QAAQ,SAAS,CACjI,CAAC;YACF,SAAS;QACX,CAAC;QAED,IAAI,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,OAAO,EAAE,CAAC;YACpE,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC1B,SAAS;QACX,CAAC;QACD,SAAS,GAAG,IAAI,CAAC;QAEjB,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;gBACxF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,MAAM,iBAAiB,CAAC;gBACxB,cAAc,GAAG,IAAI,CAAC;YACxB,CAAC;YACD,IAAI,KAAK,CAAC,KAAK,CAAC,iBAAiB,KAAK,YAAY,EAAE,CAAC;gBACnD,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC;gBAC7C,MAAM,OAAO,YAAY,MAAM,CAAC;YAClC,CAAC;YACD,MAAM,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,iBAAiB,IAAI,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACrF,CAAC;aAAM,CAAC;YACN,mEAAmE;YACnE,iBAAiB;YACjB,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,MAAM;oBACJ,aAAa;oBACb,EAAE;oBACF,oEAAoE;oBACpE,uCAAuC;iBACxC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;gBACpB,cAAc,GAAG,IAAI,CAAC;YACxB,CAAC;YACD,MAAM,QAAQ,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,MAAM,gBAAgB,CAAC,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxE,CAAC;SAAM,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACrC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM;gBACJ,aAAa;gBACb,EAAE;gBACF,KAAK,KAAK,SAAS;oBACjB,CAAC,CAAC,yBAAyB;oBAC3B,CAAC,CAAC,6DAA6D;gBACjE,EAAE;aACH,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC;YAC9B,MAAM;gBACJ,gCAAgC;gBAChC,EAAE;gBACF,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,6CAA6C,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG;gBAC/G,EAAE;gBACF,4CAA4C;gBAC5C,2BAA2B;gBAC3B,GAAG,YAAY;gBACf,EAAE;aACH,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAED,MAAM,4BAA4B,CAAC;IACnC,MAAM,eAAe,CAAC,MAAM,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;IAEtD,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IACvF,MAAM,OAAO,GAAa,CAAC,YAAY,EAAE,EAAE,EAAE,qBAAqB,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC7F,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,CACV,KAAK,KAAK,CAAC,QAAQ,SAAS,KAAK,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,KAAK,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,0BAA0B,EACpI,EAAE,CACH,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IAChE,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAE/D,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QAClE,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC;QAC5F,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC;QACxE,MAAM,OAAO,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC;QAClE,OAAO,CAAC,IAAI,CACV,mBAAmB,EACnB,EAAE,EACF,OAAO,KAAK,CAAC;YACX,CAAC,CAAC,0CAA0C;YAC5C,CAAC,CAAC,UAAU,OAAO,SAAS,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,6BAA6B;gBAC7E,KAAK,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,sBAAsB;gBAC5E,GAAG,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,kBAAkB;gBACtF,GAAG,MAAM,CAAC,QAAQ,kBAAkB,MAAM,CAAC,YAAY,yBAAyB;gBAChF,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;oBACf,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC,cAAc,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,wCAAwC,CAAC,EAC7H,EAAE,CACH,CAAC;QACF,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CACV,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,iDAAiD,EAC7E,EAAE,CACH,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,IAAI,CACV,sBAAsB,EACtB,eAAe,EACf,GAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAqB,CAAC,GAAG,CACrD,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,cAAc,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,IAAI,CACjD,EACD,EAAE,CACH,CAAC;QACF,0EAA0E;QAC1E,sEAAsE;QACtE,uEAAuE;QACvE,wEAAwE;QACxE,0BAA0B;QAC1B,IAAI,MAAM,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,IAAI,CACV,iBAAiB,EACjB,EAAE,EACF,GAAG,MAAM,CAAC,YAAY,SAAS,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,qCAAqC,EACxG,qFAAqF,EACrF,wFAAwF,EACxF,2FAA2F,EAC3F,EAAE,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAClC,CAAC;AAED,iFAAiF;AACjF,KAAK,UAAU,KAAK,CAClB,UAA0C,EAC1C,KAAgB;IAEhB,MAAM,IAAI,GAAsB,EAAE,CAAC;IACnC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QACrC,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kFAAkF;AAClF,SAAS,eAAe,CAAC,IAAgC;IACvD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,WAAW,CAAC;IAC1C,OAAO,CACL;QACE,2EAA2E;QAC3E,yCAAyC;QACzC,EAAE;QACF,8EAA8E;QAC9E,uCAAuC;QACvC,GAAG,IAAI,CAAC,GAAG,CACT,CAAC,CAAC,EAAE,EAAE,CACJ,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,gBAAgB,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,MACrG,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAC9D,MAAM,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAC5B;KACF,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CACpB,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,SAAS,gBAAgB,CACvB,WAA4D,EAC5D,KAAgB,EAChB,OAAiC;IAEjC,MAAM,QAAQ,GACZ,WAAW,CAAC,KAAK,KAAK,CAAC;QACrB,CAAC,CAAC,kDAAkD;QACpD,CAAC,CAAC,GAAG,WAAW,CAAC,KAAK,SAAS,WAAW,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAC7D,WAAW,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KACnC,oCAAoC,WAAW,CAAC,EAAE,CAAC,QAAQ,GAAG,WAAW,CAAC,EAAE,CAAC,IAAI,GAAG;YACpF,GAAG,WAAW,CAAC,EAAE,CAAC,QAAQ,GAAG,WAAW,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,oBAAoB,CAAC;IAEhG,MAAM,KAAK,GAAG,CAAC,uBAAuB,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CACR,KAAK,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,mCAAmC;YAC3F,6BAA6B,EAC/B,EAAE,CACH,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CACR,4BAA4B,EAC5B,EAAE,EACF,4BAA4B,EAC5B,eAAe,EACf,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAC5D,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAC/C,EACD,EAAE,CACH,CAAC;IACF,IAAI,WAAW,CAAC,KAAK,KAAK,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;IAEtE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CACR,+BAA+B,EAC/B,EAAE,EACF,GAAG,OAAO,CAAC,GAAG,CACZ,CAAC,KAAK,EAAE,EAAE,CACR,OAAO,KAAK,CAAC,KAAK,CAAC,iBAAiB,QAAQ,KAAK,CAAC,KAAK,CAAC,KAAK,UAAU,KAAK,CAAC,QAAQ,QAAQ,CAChG,EACD,EAAE,EACF,6EAA6E,EAC7E,EAAE,CACH,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secureport/core",
3
- "version": "2.1.0",
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.1",
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,
package/src/issue.ts CHANGED
@@ -216,6 +216,7 @@ export type IssueEventType =
216
216
  | 'commented'
217
217
  | 'assigned'
218
218
  | 'merged'
219
+ | 'retitled'
219
220
  | 'exported';
220
221
 
221
222
  /**