docs(severity): B3 — document info as scoring-inert (v7.2.0 prep)

Critical-review §2 B3 finding: `riskScore({info: N}) = 0` silently masks
info-volume findings. The behavior was correct (info is scoring-inert by
design) but undocumented. Operators reading a report with N info findings
had no way to know they contribute zero to verdict/band.

Three coordinated edits:
- scanners/lib/severity.mjs JSDoc — explicit "Info severity" subsection
  spelling out: scoring-inert, surfaced in owaspCategorize aggregates,
  treat as observability telemetry not verdict input. @param updated to
  mark info as accepted but ignored.
- CLAUDE.md v7.0.0 risk-score-v2 line — one-sentence anchor pointing to
  severity.mjs JSDoc.
- tests/lib/severity.test.mjs — anchor test alongside the existing
  4-critical=93 anchor: asserts riskScore({info: 50}) === 0,
  riskScore({info: 1000}) === 0, verdict({info: 100}) === 'ALLOW',
  riskBand(riskScore({info: 500})) === 'Low'.

Decision: skip the optional `infoScore()` helper from the brief. No
current consumer would use it; doc-only fix keeps API surface minimal.
Revisit if a consumer emerges.

Tests: 1522 → 1523 (+1 anchor block, 4 assertions). All green.
This commit is contained in:
Kjell Tore Guttormsen 2026-04-29 13:56:11 +02:00
commit 3cd68dc9fb
3 changed files with 23 additions and 2 deletions

View file

@ -26,7 +26,18 @@ const SEVERITY_WEIGHTS_V1 = { critical: 25, high: 10, medium: 4, low: 1, info: 0
* Low only 1-11 (1=4, 10=11)
* None 0
*
* @param {{ critical: number, high: number, medium: number, low: number, info: number }} counts
* Info severity (B3, v7.2.0):
* `info` counts are scoring-inert accepted in the input shape but
* ignored by this formula. They contribute 0 to risk_score, do not
* affect verdict (BLOCK/WARNING/ALLOW), and do not affect riskBand
* (Low/Medium/High/Critical/Extreme). They ARE surfaced in
* `owaspCategorize` aggregates and in scanner report bodies for
* observability. Operators reading a report with N info findings
* should treat them as informational telemetry, not as input to
* the verdict.
*
* @param {{ critical: number, high: number, medium: number, low: number, info?: number }} counts
* `info` is accepted for shape completeness but ignored see above.
* @returns {number} 0-100 risk score
*/
export function riskScore(counts) {