@secureport/core 2.1.0 → 2.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/report/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE9D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEtE,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B,mCAAmC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,gFAAgF;IAChF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B,qBAAqB;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,YAAY;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IAEjC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAE5B,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAEpC,oCAAoC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;CAC3C;AAED,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC1B,4BAA4B;IAC5B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,4BAA4B;IAC5B,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAE3B,kCAAkC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B,gCAAgC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAE7B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAE5C;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAE5C,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IAEzD,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAElC,sCAAsC;IACtC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAE/B,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAExC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;CACjC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,eAAe,GAAG,UAAU,GAAG,cAAc,GAAG,KAAK,CAAC;AAE5F,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAE9B,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;CACjC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAE9B,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAEzC,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IAEzD;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AA6ID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,GAAG,WAAW,CAwExF"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/report/model.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,21 @@
1
1
  import { SEVERITY_ORDER } from '../severity.js';
2
+ /**
3
+ * How each verdict is printed.
4
+ *
5
+ * Exported because it is printed, and a printed vocabulary is a contract —
6
+ * `00-DOMAIN.md` §7 says so of the verdicts themselves. It was defined
7
+ * identically in both renderers, which PDF and DOCX would have made four
8
+ * copies of, each one a chance for `not_retested` to be worded differently in
9
+ * the format somebody actually reads. The underscores are an implementation
10
+ * detail of the union and should never reach a page.
11
+ */
12
+ export const VERDICT_LABELS = Object.freeze({
13
+ fixed: 'fixed',
14
+ still_present: 'still present',
15
+ returned: 'returned',
16
+ not_retested: 'not retested',
17
+ new: 'new since',
18
+ });
2
19
  /** Works out what a run established about the issues carried into it. */
3
20
  function describeRetest(snapshot) {
4
21
  const { baseline } = snapshot;
@@ -54,7 +71,7 @@ function verdictFor(entry) {
54
71
  return entry.findings.length > 0 ? 'still_present' : 'not_retested';
55
72
  }
56
73
  /** Works out what a report cannot establish, from the run rather than a template. */
57
- function describeLimitations(snapshot, basis) {
74
+ function describeLimitations(snapshot, basis, omitted) {
58
75
  const limitations = [];
59
76
  if (!basis.includesManual) {
60
77
  limitations.push('This is evidence of automated security testing. It is not a human-led penetration ' +
@@ -73,9 +90,20 @@ function describeLimitations(snapshot, basis) {
73
90
  limitations.push(`${snapshot.suppressed.length} finding${snapshot.suppressed.length === 1 ? ' has' : 's have'} been ` +
74
91
  'suppressed and excluded from the counts above. They are listed in full in the appendix.');
75
92
  }
93
+ // Immediately after the suppression sentence and before the compliance one,
94
+ // because it answers the same question a reader is entitled to ask: what am
95
+ // I not being shown? A floor that removed pages silently would be the
96
+ // over-claim §7 exists to stop, in a quieter form than the appendix one.
97
+ if (omitted !== undefined)
98
+ limitations.push(omitted.statement);
76
99
  limitations.push('This document does not certify compliance with any standard, framework or regulation.');
77
100
  return limitations;
78
101
  }
102
+ /**
103
+ * Hex triplets only. See {@link Branding.primaryColour} for why this is a
104
+ * refusal rather than a sanitisation.
105
+ */
106
+ const HEX_COLOUR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/iu;
79
107
  const DEFAULT_TITLES = Object.freeze({
80
108
  pen: 'Penetration Test Report',
81
109
  vap: 'Vulnerability Assessment Report',
@@ -138,22 +166,36 @@ export function buildReportModel(snapshot, options) {
138
166
  const bySeverity = new Map();
139
167
  const resolved = [];
140
168
  let breached = 0;
169
+ let omittedBelowFloor = 0;
170
+ // `SEVERITY_ORDER` is most-severe-first, so "at or above the floor" is a
171
+ // *lower* index. Same trap `describeRetest` records above, from the other
172
+ // direction: `severityRank` counts the opposite way and using it here would
173
+ // floor out everything except the advisories.
174
+ const floorAt = options.severityFloor === undefined
175
+ ? SEVERITY_ORDER.length
176
+ : SEVERITY_ORDER.indexOf(options.severityFloor);
141
177
  for (const entry of snapshot.issues) {
142
178
  if (entry.issue.status === 'resolved') {
143
179
  resolved.push(entry);
144
180
  continue;
145
181
  }
146
182
  const severity = entry.issue.effectiveSeverity;
147
- const group = bySeverity.get(severity);
148
- if (group)
149
- group.push(entry);
150
- else
151
- bySeverity.set(severity, [entry]);
183
+ // Counted before the floor is consulted, deliberately: the floor decides
184
+ // what is *listed*, never what is *counted*.
152
185
  if (entry.issue.status === 'open' || entry.issue.status === 'regressed') {
153
186
  outstanding[severity]++;
154
187
  if (entry.slaStatus === 'breached')
155
188
  breached++;
156
189
  }
190
+ if (SEVERITY_ORDER.indexOf(severity) > floorAt) {
191
+ omittedBelowFloor++;
192
+ continue;
193
+ }
194
+ const group = bySeverity.get(severity);
195
+ if (group)
196
+ group.push(entry);
197
+ else
198
+ bySeverity.set(severity, [entry]);
157
199
  }
158
200
  resolved.sort((a, b) => b.issue.lastSeen.getTime() - a.issue.lastSeen.getTime());
159
201
  const sections = [];
@@ -168,6 +210,17 @@ export function buildReportModel(snapshot, options) {
168
210
  issues: [...issues].sort((a, b) => b.issue.lastSeen.getTime() - a.issue.lastSeen.getTime()),
169
211
  });
170
212
  }
213
+ const floor = options.severityFloor;
214
+ const omitted = omittedBelowFloor === 0 || floor === undefined
215
+ ? undefined
216
+ : {
217
+ count: omittedBelowFloor,
218
+ floor,
219
+ statement: `${omittedBelowFloor} issue${omittedBelowFloor === 1 ? '' : 's'} below ${floor} severity ` +
220
+ `${omittedBelowFloor === 1 ? 'is' : 'are'} not listed individually in this document. ` +
221
+ `${omittedBelowFloor === 1 ? 'It remains' : 'They remain'} open and ` +
222
+ `${omittedBelowFloor === 1 ? 'is' : 'are'} included in every count above.`,
223
+ };
171
224
  const outstandingTotal = SEVERITY_ORDER.reduce((sum, s) => sum + outstanding[s], 0);
172
225
  const basis = describeBasis(snapshot);
173
226
  const retest = describeRetest(snapshot);
@@ -176,6 +229,26 @@ export function buildReportModel(snapshot, options) {
176
229
  if (options.kind === 'retest' && retest === undefined) {
177
230
  throw new TypeError('a retest report needs a baseline: build the snapshot with `previous` set to the run being retested');
178
231
  }
232
+ const branding = options.branding;
233
+ const colour = branding?.primaryColour;
234
+ if (colour !== undefined && !HEX_COLOUR.test(colour)) {
235
+ throw new TypeError(`primaryColour must be a hex triplet such as #0a7 or #00aa77, not ${JSON.stringify(colour)}: ` +
236
+ 'it is interpolated into the document stylesheet, where anything else could end the element');
237
+ }
238
+ const logo = branding?.logo;
239
+ if (logo !== undefined && !logo.startsWith('data:image/')) {
240
+ throw new TypeError(`logo must be a data: URI, not ${JSON.stringify(logo.slice(0, 40))}: ` +
241
+ 'a linked image would make the report depend on a network at render time, ' +
242
+ 'which is the guarantee that lets it be rendered to PDF reproducibly');
243
+ }
244
+ const attestor = options.preparedBy ?? branding?.companyName;
245
+ // An attestation is a statement that a named party carried out testing. With
246
+ // Secureport's name removed and nothing put in its place there is no such
247
+ // party, and the document would assert something on nobody's behalf.
248
+ if (attestor === undefined && branding?.whiteLabel === true && options.kind === 'attest') {
249
+ throw new TypeError('a white-labelled attestation must name who carried out the testing: ' +
250
+ 'set branding.companyName or preparedBy');
251
+ }
179
252
  return {
180
253
  kind: options.kind,
181
254
  title: options.title ?? DEFAULT_TITLES[options.kind],
@@ -183,10 +256,16 @@ export function buildReportModel(snapshot, options) {
183
256
  generatedAt: options.now,
184
257
  ...(options.preparedBy === undefined ? {} : { preparedBy: options.preparedBy }),
185
258
  ...(options.preparedFor === undefined ? {} : { preparedFor: options.preparedFor }),
259
+ attestor: attestor ?? 'Secureport',
260
+ coverPage: options.coverPage === true,
261
+ tableOfContents: options.tableOfContents === true,
262
+ ...(branding === undefined ? {} : { branding }),
186
263
  basis,
187
- limitations: describeLimitations(snapshot, basis),
264
+ evidenceVerbosity: options.evidenceVerbosity ?? 'summary',
265
+ limitations: describeLimitations(snapshot, basis, omitted),
188
266
  sections,
189
- resolved,
267
+ resolved: options.includeResolved === false ? [] : resolved,
268
+ ...(omitted === undefined ? {} : { omitted }),
190
269
  outstanding,
191
270
  outstandingTotal,
192
271
  exposureScore: snapshot.run.exposureScore,
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/report/model.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAwOhD,yEAAyE;AACzE,SAAS,cAAc,CAAC,QAAkB;IACxC,MAAM,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC;IAC9B,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAE7C,MAAM,OAAO,GAAkB,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7D,KAAK;QACL,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC;KAC3B,CAAC,CAAC,CAAC;IACJ,2EAA2E;IAC3E,0EAA0E;IAC1E,4EAA4E;IAC5E,qDAAqD;IACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACpB,MAAM,UAAU,GACd,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC;YACvD,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC1D,OAAO,UAAU,KAAK,CAAC;YACrB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE;YACrE,CAAC,CAAC,UAAU,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAkC;QAC5C,KAAK,EAAE,CAAC;QACR,aAAa,EAAE,CAAC;QAChB,QAAQ,EAAE,CAAC;QACX,YAAY,EAAE,CAAC;QACf,GAAG,EAAE,CAAC;KACP,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,OAAO;QAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;IAErD,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC;IACxE,OAAO;QACL,QAAQ;QACR,OAAO;QACP,MAAM;QACN,OAAO,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,SAAS;KAC3D,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,UAAU,CAAC,KAAoB;IACtC,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW;QAAE,OAAO,UAAU,CAAC;IACpD,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACzC,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC;AACtE,CAAC;AAED,qFAAqF;AACrF,SAAS,mBAAmB,CAAC,QAAkB,EAAE,KAAmB;IAClE,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;QAC1B,WAAW,CAAC,IAAI,CACd,oFAAoF;YAClF,+CAA+C,CAClD,CAAC;IACJ,CAAC;IACD,WAAW,CAAC,IAAI,CACd,uFAAuF;QACrF,yBAAyB,CAC5B,CAAC;IACF,WAAW,CAAC,IAAI,CACd,wBAAwB,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B;QACtF,wFAAwF,CAC3F,CAAC;IACF,WAAW,CAAC,IAAI,CACd,+CAA+C,QAAQ,CAAC,GAAG,CAAC,SAAS;SAClE,WAAW,EAAE;SACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,+DAA+D,CAC/E,CAAC;IACF,WAAW,CAAC,IAAI,CACd,0FAA0F;QACxF,kDAAkD,CACrD,CAAC;IACF,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,WAAW,CAAC,IAAI,CACd,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,WAAW,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,QAAQ;YAClG,yFAAyF,CAC5F,CAAC;IACJ,CAAC;IACD,WAAW,CAAC,IAAI,CACd,uFAAuF,CACxF,CAAC;IAEF,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,cAAc,GAAyC,MAAM,CAAC,MAAM,CAAC;IACzE,GAAG,EAAE,yBAAyB;IAC9B,GAAG,EAAE,iCAAiC;IACtC,IAAI,EAAE,mBAAmB;IACzB,MAAM,EAAE,iCAAiC;IACzC,MAAM,EAAE,eAAe;CACxB,CAAC,CAAC;AAEH,kFAAkF;AAClF,SAAS,aAAa,CAAC,QAAkB;IACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;IAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;IACrC,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;IAChF,MAAM,SAAS,GAAG,IAAI,KAAK,MAAM,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,KAAK,QAAQ,CAAC;IAEnC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,CAAC,IAAI,CACR,+FAA+F,CAChG,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CACR,4FAA4F,CAC7F,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;IAClF,IAAI,cAAc,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,CAAC,cAAc,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACzC,KAAK,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CACR,wFAAwF;YACtF,8BAA8B,CACjC,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACtF,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAkB,EAAE,OAAsB;IACzE,MAAM,WAAW,GAA6B;QAC5C,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,CAAC;QACN,QAAQ,EAAE,CAAC;KACZ,CAAC;IAEF,MAAM,UAAU,GAAG,IAAI,GAAG,EAA6B,CAAC;IACxD,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YACtC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC;QAC/C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;YACxB,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAEvC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACxE,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxB,IAAI,KAAK,CAAC,SAAS,KAAK,UAAU;gBAAE,QAAQ,EAAE,CAAC;QACjD,CAAC;IACH,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAEjF,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,KAAK,MAAM,QAAQ,IAAI,cAAc,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC1D,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ;YACR,wEAAwE;YACxE,yCAAyC;YACzC,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;SAC5F,CAAC,CAAC;IACL,CAAC;IAED,MAAM,gBAAgB,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpF,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAExC,sEAAsE;IACtE,2EAA2E;IAC3E,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACtD,MAAM,IAAI,SAAS,CACjB,oGAAoG,CACrG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC;QACpD,QAAQ;QACR,WAAW,EAAE,OAAO,CAAC,GAAG;QACxB,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;QAC/E,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;QAClF,KAAK;QACL,WAAW,EAAE,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC;QACjD,QAAQ;QACR,QAAQ;QACR,WAAW;QACX,gBAAgB;QAChB,aAAa,EAAE,QAAQ,CAAC,GAAG,CAAC,aAAa;QACzC,QAAQ;QACR,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;KAC5C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/report/model.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.1.0",
3
+ "version": "2.2.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",
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,12 @@ export type {
62
65
  RetestVerdict,
63
66
  TestingBasis,
64
67
  } from './report/model.js';
65
- export { buildReportModel } from './report/model.js';
68
+ export { buildReportModel, VERDICT_LABELS } from './report/model.js';
66
69
  export { renderMarkdown } from './report/markdown.js';
67
70
  export { renderHtml } from './report/html.js';
68
71
  export type { JsonReport } from './report/json.js';
69
72
  export { renderJson } from './report/json.js';
70
- export type { ReconcileInput, ReconcileResult } from './reconcile.js';
73
+ export type { ReconcileInput, ReconcileResult, SuppressionDecision } from './reconcile.js';
71
74
  export { reconcile } from './reconcile.js';
72
75
  export type {
73
76
  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
  /**
package/src/reconcile.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Finding } from './finding.js';
2
2
  import { FINGERPRINT_VERSION } from './fingerprint.js';
3
- import type { Issue, IssueEvent } from './issue.js';
3
+ import type { IgnoreReason, IgnoreScope, Issue, IssueEvent } from './issue.js';
4
4
  import type { Run, RunKind, RunSummary, SeverityCounts } from './run.js';
5
5
  import {
6
6
  DEFAULT_SLA_POLICY,
@@ -73,6 +73,56 @@ export interface ReconcileInput {
73
73
 
74
74
  /** How long the run took, for the summary. */
75
75
  readonly durationMs?: number;
76
+
77
+ /**
78
+ * Consulted for each issue this run would newly open, to suppress it at
79
+ * birth — a standing rule rather than a decision about one issue.
80
+ *
81
+ * **It has to live here, and B89 records why the obvious alternative fails.**
82
+ * `reconcile()` computes the run summary, so an issue it opens is counted
83
+ * `new`. A caller that suppressed that issue after the fact would produce a
84
+ * report whose opening line said `new` while its own appendix said
85
+ * `suppressed`, about the same issue, in the same document. Filtering the
86
+ * findings before reconciling is no better: the issue then vanishes from the
87
+ * summary entirely rather than being suppressed, and invariant 7 says a
88
+ * suppressed issue is never absent from the appendix.
89
+ *
90
+ * Return `undefined` to open the issue normally. The suppressed issue is
91
+ * counted `ignored` in the same pass that counts everything else, which the
92
+ * summary already does correctly because it tests `status` before it tests
93
+ * whether the run created the issue.
94
+ *
95
+ * **Consulted only for new issues**, never for one that already exists: an
96
+ * issue a person has looked at is theirs, and a rule written afterwards does
97
+ * not get to reach back and hide it.
98
+ */
99
+ readonly suppress?: (issue: Issue) => SuppressionDecision | undefined;
100
+ }
101
+
102
+ /**
103
+ * Why a standing rule suppressed an issue at the moment it was opened.
104
+ *
105
+ * @see {@link ReconcileInput.suppress}
106
+ */
107
+ export interface SuppressionDecision {
108
+ /** Which of the domain's ignore reasons applies. */
109
+ readonly reason: IgnoreReason;
110
+
111
+ /** The justification an auditor reads in the suppressed appendix. */
112
+ readonly comment?: string;
113
+
114
+ /** How widely the suppression applies. */
115
+ readonly scope?: IgnoreScope;
116
+
117
+ /**
118
+ * Who accepted the risk. Defaults to {@link ReconcileInput.actor}.
119
+ *
120
+ * Worth passing. The appendix is an accepted-risk register, and a register
121
+ * whose every row says `reconciler` answers "who accepted this?" with "the
122
+ * clock did" — which `00-DOMAIN.md` §3 is explicit is not an identity. The
123
+ * rule that matched has an author; this is where to name them.
124
+ */
125
+ readonly by?: string;
76
126
  }
77
127
 
78
128
  /**
@@ -319,8 +369,42 @@ export function reconcile(input: ReconcileInput): ReconcileResult {
319
369
  origin: run.kind,
320
370
  slaDueAt: slaDueAt(worst.detectedSeverity, now, slaPolicy),
321
371
  };
322
- updated.set(key, issue);
372
+ const decision = input.suppress?.(issue);
373
+ if (decision === undefined) {
374
+ updated.set(key, issue);
375
+ emit(id, 'created', { findingIds, severity: worst.detectedSeverity });
376
+ continue;
377
+ }
378
+
379
+ // Both events, in order. The issue was opened and then suppressed; a
380
+ // history that recorded only the suppression would have an issue whose
381
+ // first event is `ignored`, and invariant 2 wants every state change
382
+ // accounted for rather than the net result.
383
+ //
384
+ // `severityAtIgnore` is set deliberately, which is what makes a rule
385
+ // liftable: the existing branch above un-ignores an issue whose detected
386
+ // severity rises past what was accepted, and a standing rule should not
387
+ // be more permanent than a person's own decision. Accepting the risk of
388
+ // a medium is not accepting the risk of a critical, however the
389
+ // acceptance was expressed. **A merge survivor is the deliberate
390
+ // exception and omits it** (P6, B98) — that is a statement about
391
+ // identity rather than risk, and it has to survive a severity rise.
392
+ const suppressed: Issue = {
393
+ ...issue,
394
+ status: 'ignored',
395
+ ignoreReason: decision.reason,
396
+ ...(decision.comment === undefined ? {} : { ignoreComment: decision.comment }),
397
+ ...(decision.scope === undefined ? {} : { ignoreScope: decision.scope }),
398
+ ignoredBy: decision.by ?? actor,
399
+ severityAtIgnore: worst.detectedSeverity,
400
+ };
401
+ updated.set(key, suppressed);
323
402
  emit(id, 'created', { findingIds, severity: worst.detectedSeverity });
403
+ emit(id, 'ignored', {
404
+ reason: decision.reason,
405
+ ...(decision.comment === undefined ? {} : { comment: decision.comment }),
406
+ bornSuppressed: true,
407
+ });
324
408
  continue;
325
409
  }
326
410
 
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Heading anchors and the contents list, produced together.
3
+ *
4
+ * **One pass over the rendered body, not two walks over the document.** A
5
+ * contents list built separately from the ids it links to is a list that can
6
+ * point at nothing — the failure is silent, it only shows up when somebody
7
+ * clicks, and it would appear the moment a heading's wording changed in one
8
+ * place and not the other. Here the ids and the entries come out of the same
9
+ * scan, so a heading that exists has an anchor and an entry, and one that does
10
+ * not exist has neither.
11
+ *
12
+ * This post-processes the renderer's **own** output, inside the same function
13
+ * that produced it. That is a different thing from the string surgery P7.1
14
+ * rules out: the rule there is against forking a published template, and this
15
+ * never reads one — every heading it matches was emitted a few lines earlier by
16
+ * the renderer calling it.
17
+ */
18
+
19
+ /** One line of the contents list. */
20
+ export interface TocEntry {
21
+ /** Heading level, 2 or 3. */
22
+ readonly level: number;
23
+
24
+ /** The anchor it links to, without the `#`. */
25
+ readonly id: string;
26
+
27
+ /** The heading as printed. */
28
+ readonly text: string;
29
+ }
30
+
31
+ /**
32
+ * A heading's anchor: lowercase, punctuation dropped, spaces hyphenated.
33
+ *
34
+ * The same rule GitHub and most Markdown renderers use, so a Markdown
35
+ * report's contents links resolve in the places Markdown is usually read —
36
+ * which is the only way this can work at all, because Markdown headings carry
37
+ * no explicit id to point at.
38
+ */
39
+ function slug(text: string): string {
40
+ return (
41
+ text
42
+ .toLowerCase()
43
+ // Entities first: a heading rendered as `AT&T` should anchor on the
44
+ // text a reader sees, not on the escape that produced it.
45
+ .replace(/&[a-z]+;/gu, ' ')
46
+ .replace(/[^a-z0-9]+/gu, '-')
47
+ .replace(/^-+|-+$/gu, '') || 'section'
48
+ );
49
+ }
50
+
51
+ /** Makes `id` unique within a document, the way a duplicate heading is usually handled. */
52
+ function unique(id: string, taken: Map<string, number>): string {
53
+ const seen = taken.get(id) ?? 0;
54
+ taken.set(id, seen + 1);
55
+ return seen === 0 ? id : `${id}-${String(seen)}`;
56
+ }
57
+
58
+ /**
59
+ * Gives every `<h2>` and `<h3>` an id, and reports what it found.
60
+ *
61
+ * `<h4>` is deliberately left alone: it is an individual issue, and a report
62
+ * with sixty findings would have a contents list longer than its summary.
63
+ */
64
+ export function anchorHtmlHeadings(html: string): { html: string; toc: TocEntry[] } {
65
+ const toc: TocEntry[] = [];
66
+ const taken = new Map<string, number>();
67
+
68
+ // Safe over this input because it is this package's own markup rather than
69
+ // anything from outside: h2 and h3 are emitted with plain escaped text and
70
+ // never with nested elements.
71
+ const anchored = html.replace(
72
+ /<h([23])([^>]*)>([\s\S]*?)<\/h\1>/gu,
73
+ (_match, level: string, attrs: string, text: string) => {
74
+ const id = unique(slug(text), taken);
75
+ toc.push({ level: Number(level), id, text });
76
+ return `<h${level}${attrs} id="${id}">${text}</h${level}>`;
77
+ },
78
+ );
79
+
80
+ return { html: anchored, toc };
81
+ }
82
+
83
+ /** The same, for Markdown, where the anchor is implied by the heading text. */
84
+ export function markdownHeadings(lines: readonly string[]): TocEntry[] {
85
+ const toc: TocEntry[] = [];
86
+ const taken = new Map<string, number>();
87
+
88
+ for (const line of lines) {
89
+ const match = /^(#{2,3})\s+(.*)$/u.exec(line);
90
+ if (!match) continue;
91
+ const text = match[2];
92
+ toc.push({ level: match[1].length, id: unique(slug(text), taken), text });
93
+ }
94
+
95
+ return toc;
96
+ }