From 8a5c712ef0007bb61d853c4b1f76aa5e6268b879 Mon Sep 17 00:00:00 2001 From: David Duran Rosich Date: Sat, 24 Oct 2026 23:55:11 +0200 Subject: [PATCH] more changes --- README.md | 13 +++++++------ scan.py | 22 ++++++++++++---------- tests/test_scan.py | 12 +++++++----- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 3e0c01e..6655aa9 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,13 @@ host only needs Docker. JSON reports are kept on disk for auditing. alert. - `FAIL_ON` (default `HIGH`) – when the script should exit with code `1` (useful for CI). -3. If any CVE reaches `NOTIFY_MIN_SEVERITY`, notifications are sent. When - `IMPORTANT_CONTAINERS` is set, each named container gets its own complete - CVE list (split into fixable and unfixable), missing important containers - are reported, and every other container is summarised in one notification. - Without `IMPORTANT_CONTAINERS`, a single aggregated notification lists each - CVE with the affected packages, containers, and images. +3. Notifications are sent. When `IMPORTANT_CONTAINERS` is set, each named + container gets its own complete CVE list (showing `C X/Y ...` where X is + fixable and Y is total per severity, plus `Total: fixable/total`), missing + important containers are reported, and every other container is summarised as + `fixable/total`. Without `IMPORTANT_CONTAINERS`, a single aggregated + notification lists each CVE with the affected packages, containers, and + images. ## Quick start diff --git a/scan.py b/scan.py index b36b8b1..f6ead8d 100755 --- a/scan.py +++ b/scan.py @@ -854,7 +854,7 @@ def build_body( MAX_NOTIFICATION_CHARS = 2500 -DISCORD_MAX_NOTIFICATION_CHARS = 1800 +DISCORD_MAX_NOTIFICATION_CHARS = 1500 def uses_discord(settings: Settings) -> bool: @@ -973,12 +973,19 @@ def build_important_body( entries = cve_entries(result, severity_rank(min_severity)) total, fixable, unfixable = split_counts(entries) counts = severity_counts(entries) + fixable_counts = {severity: 0 for severity in REPORTED_SEVERITIES} + for entry in entries: + if entry.fixable: + fixable_counts[entry.severity] += 1 lines = [ f"Image: {target.image}", f"Container: {container}", - " | ".join(f"{name.title()}: {counts[name]}" for name in REPORTED_SEVERITIES), - f"Total: {total} (fixable: {fixable}, unfixable: {unfixable})", + " | ".join( + f"{name[0]} {fixable_counts[name]}/{counts[name]}" + for name in REPORTED_SEVERITIES + ), + f"Total: {fixable}/{total} (fixable/total)", "", "CVEs:", ] @@ -1000,15 +1007,10 @@ def build_other_containers_body( Each row is ``(image, container, total, fixable, unfixable)``. """ lines = ["Other containers with findings:", ""] - total_all = 0 for image, container, total, fixable, unfixable in rows: - total_all += total label = f"{image} [{container}]" if container else image - lines.append( - f"* {label}: {total} total " - f"({fixable} fixable, {unfixable} unfixable)" - ) - lines += ["", f"Total: {total_all} CVE(s) in {len(rows)} container(s)"] + lines.append(f"* {label}: {fixable}/{total} (fixable/total)") + lines += ["", f"Total: {sum(row[2] for row in rows)} CVE(s) in {len(rows)} container(s)"] return "\n".join(lines) diff --git a/tests/test_scan.py b/tests/test_scan.py index f708b98..2b39c53 100644 --- a/tests/test_scan.py +++ b/tests/test_scan.py @@ -475,7 +475,8 @@ class ReportTests(unittest.TestCase): ) self.assertIn("Image: nginx:1.27", body) self.assertIn("Container: web", body) - self.assertIn("Total: 3 (fixable: 2, unfixable: 1)", body) + self.assertIn("C 1/1 | H 1/2 | M 0/0 | L 0/0", body) + self.assertIn("Total: 2/3 (fixable/total)", body) self.assertIn("CVE-1", body) self.assertIn("CVE-2", body) self.assertIn("CVE-5", body) @@ -491,7 +492,8 @@ class ReportTests(unittest.TestCase): self.assertIn("CVE-1", body) self.assertNotIn("CVE-2", body) self.assertNotIn("CVE-5", body) - self.assertIn("Total: 1 (fixable: 1, unfixable: 0)", body) + self.assertIn("C 1/1 | H 0/0 | M 0/0 | L 0/0", body) + self.assertIn("Total: 1/1 (fixable/total)", body) def test_build_other_containers_body_has_counts_per_container(self) -> None: body = scan.build_other_containers_body( @@ -500,8 +502,8 @@ class ReportTests(unittest.TestCase): ("redis:7", "cache", 4, 0, 4), ] ) - self.assertIn("* nginx:1.27 [web-worker]: 3 total (2 fixable, 1 unfixable)", body) - self.assertIn("* redis:7 [cache]: 4 total (0 fixable, 4 unfixable)", body) + self.assertIn("* nginx:1.27 [web-worker]: 2/3 (fixable/total)", body) + self.assertIn("* redis:7 [cache]: 0/4 (fixable/total)", body) self.assertIn("Total: 7 CVE(s) in 2 container(s)", body) def test_build_missing_body_separates_missing_and_unscanned(self) -> None: @@ -667,7 +669,7 @@ printf '%s\\n' "$*" >> "$APPRISE_LOG" self.assertIn("CVE-2026-0001", notification) self.assertIn("Vulnerability scan: other containers", notification) self.assertIn("redis:7 [cache]", notification) - self.assertIn("(1 fixable, 0 unfixable)", notification) + self.assertIn("1/1 (fixable/total)", notification) # The important container must not also appear in the summary. self.assertNotIn("testphp-php [web]", notification)