From f775e1f1edd95c9d9c34e8d26a88c5e16c10d85b Mon Sep 17 00:00:00 2001 From: Florian Frank Date: Mon, 21 Sep 2026 22:09:01 +0200 Subject: [PATCH] Improve naming different metrics. --- report_generator/context.py | 37 ++++++++++++++---------- report_generator/template/report.html.j2 | 5 ++-- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/report_generator/context.py b/report_generator/context.py index 1d51807..813c3c9 100644 --- a/report_generator/context.py +++ b/report_generator/context.py @@ -270,22 +270,34 @@ def _metric_cards(dependencies: dict[str, Any]) -> list[dict[str, Any]]: return [ { "title": "Severity & impact", + "note": "CVSS is the mean base score of the scored dependency findings. Impact values are normalized averages from 0 to 10; higher means greater potential damage.", "rows": [ - _fact("CVSS", _metric(scores.get("cvss"), "mean")), - _fact("Severity band", (scores.get("cvss") or {}).get("band"), kind="severity"), - _fact("Confidentiality", _metric(impact.get("scores"), "confidentiality")), - _fact("Integrity", _metric(impact.get("scores"), "integrity")), - _fact("Availability", _metric(impact.get("scores"), "availability")), + _fact("Average CVSS score (0-10)", _metric(scores.get("cvss"), "mean")), + _fact("Average CVSS severity", (scores.get("cvss") or {}).get("band"), kind="severity"), + _fact("Average confidentiality impact (0-10)", _metric(impact.get("scores"), "confidentiality")), + _fact("Average integrity impact (0-10)", _metric(impact.get("scores"), "integrity")), + _fact("Average availability impact (0-10)", _metric(impact.get("scores"), "availability")), ], }, { "title": "Exploitability", - "rows": [_fact(_label(axis), _metric(exploitability.get(axis), "score")) for axis in ( - "attack_vector", "attack_complexity", "privileges_required", "user_interaction" - )], + "note": "These are normalized averages of the CVSS vector dimensions across scored findings. Higher values indicate fewer obstacles for an attacker, not a probability of exploitation.", + "rows": [ + _fact( + label, + _metric(exploitability.get(axis), "score"), + ) + for axis, label in ( + ("attack_vector", "Average attack vector exposure (0-10)"), + ("attack_complexity", "Average attack complexity exposure (0-10)"), + ("privileges_required", "Average privilege exposure (0-10)"), + ("user_interaction", "Average user-interaction exposure (0-10)"), + ) + ], }, { "title": "Temporal", + "note": "Published dates and vulnerability age are calculated from the advisory timestamps. Time-to-exploit and volume trends require historical exploitation or scan data that this export does not contain.", "rows": [ _fact("Newest published", _published(published.get("newest"))), _fact("Mean vulnerability age", _days(published.get("mean_days"))), @@ -296,10 +308,9 @@ def _metric_cards(dependencies: dict[str, Any]) -> list[dict[str, Any]]: }, { "title": "Surface & taxonomy", + "note": "Surface counts describe the packages represented by the findings. CWE coverage counts findings with a CWE classification; it does not measure CWE quality or abstraction depth.", "rows": [ - _fact("Affected products", surface.get("products")), - _fact("Affected versions", surface.get("versions")), - _fact("Vendors", ", ".join(surface.get("vendors") or []) or None), + _fact("Affected dependencies", surface.get("products")), _fact("Potential remedies", _remedy(upgrades)), _fact("CWE coverage", taxonomy.get("coverage")), _fact("CWE abstraction level", taxonomy.get("abstraction_level")), @@ -329,10 +340,6 @@ def _remedy(value: dict[str, Any]) -> str | None: return None -def _label(value: str) -> str: - return value.replace("_", " ").title() - - def _issue_types(sast: SarifReport | None, sca: VdrReport | None) -> list[str]: """How many distinct kinds of issue each half of the pipeline found.""" counted = [] diff --git a/report_generator/template/report.html.j2 b/report_generator/template/report.html.j2 index 41451d7..04e263d 100644 --- a/report_generator/template/report.html.j2 +++ b/report_generator/template/report.html.j2 @@ -32,7 +32,7 @@ {% endmacro %} -{% macro card(title, rows) %} +{% macro card(title, rows, note="") %} {# Wrapped, because break-inside on the table does not hold its caption: the title would stay on one page while the rows it titles moved to the next. #}
@@ -45,6 +45,7 @@ {% endfor %} + {% if note %}

{{ note }}

{% endif %}
{% endmacro %} @@ -158,7 +159,7 @@ {% endfor %} {% for metric in summary.metrics or [] %} - {{ card(metric.title, metric.rows) }} + {{ card(metric.title, metric.rows, metric.note) }} {% endfor %} {% endif %}