more changes
This commit is contained in:
1 parent
a8241fa648
commit
8a5c712ef0
3 files changed
+26
-21
No files matched your search
@@ -25,12 +25,13 @@ host only needs Docker. JSON reports are kept on disk for auditing.
|
|||||||
alert.
|
alert.
|
||||||
- `FAIL_ON` (default `HIGH`) – when the script should exit with code `1`
|
- `FAIL_ON` (default `HIGH`) – when the script should exit with code `1`
|
||||||
(useful for CI).
|
(useful for CI).
|
||||||
3. If any CVE reaches `NOTIFY_MIN_SEVERITY`, notifications are sent. When
|
3. Notifications are sent. When `IMPORTANT_CONTAINERS` is set, each named
|
||||||
`IMPORTANT_CONTAINERS` is set, each named container gets its own complete
|
container gets its own complete CVE list (showing `C X/Y ...` where X is
|
||||||
CVE list (split into fixable and unfixable), missing important containers
|
fixable and Y is total per severity, plus `Total: fixable/total`), missing
|
||||||
are reported, and every other container is summarised in one notification.
|
important containers are reported, and every other container is summarised as
|
||||||
Without `IMPORTANT_CONTAINERS`, a single aggregated notification lists each
|
`fixable/total`. Without `IMPORTANT_CONTAINERS`, a single aggregated
|
||||||
CVE with the affected packages, containers, and images.
|
notification lists each CVE with the affected packages, containers, and
|
||||||
|
images.
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
|
|||||||
@@ -854,7 +854,7 @@ def build_body(
|
|||||||
|
|
||||||
|
|
||||||
MAX_NOTIFICATION_CHARS = 2500
|
MAX_NOTIFICATION_CHARS = 2500
|
||||||
DISCORD_MAX_NOTIFICATION_CHARS = 1800
|
DISCORD_MAX_NOTIFICATION_CHARS = 1500
|
||||||
|
|
||||||
|
|
||||||
def uses_discord(settings: Settings) -> bool:
|
def uses_discord(settings: Settings) -> bool:
|
||||||
@@ -973,12 +973,19 @@ def build_important_body(
|
|||||||
entries = cve_entries(result, severity_rank(min_severity))
|
entries = cve_entries(result, severity_rank(min_severity))
|
||||||
total, fixable, unfixable = split_counts(entries)
|
total, fixable, unfixable = split_counts(entries)
|
||||||
counts = severity_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 = [
|
lines = [
|
||||||
f"Image: {target.image}",
|
f"Image: {target.image}",
|
||||||
f"Container: {container}",
|
f"Container: {container}",
|
||||||
" | ".join(f"{name.title()}: {counts[name]}" for name in REPORTED_SEVERITIES),
|
" | ".join(
|
||||||
f"Total: {total} (fixable: {fixable}, unfixable: {unfixable})",
|
f"{name[0]} {fixable_counts[name]}/{counts[name]}"
|
||||||
|
for name in REPORTED_SEVERITIES
|
||||||
|
),
|
||||||
|
f"Total: {fixable}/{total} (fixable/total)",
|
||||||
"",
|
"",
|
||||||
"CVEs:",
|
"CVEs:",
|
||||||
]
|
]
|
||||||
@@ -1000,15 +1007,10 @@ def build_other_containers_body(
|
|||||||
Each row is ``(image, container, total, fixable, unfixable)``.
|
Each row is ``(image, container, total, fixable, unfixable)``.
|
||||||
"""
|
"""
|
||||||
lines = ["Other containers with findings:", ""]
|
lines = ["Other containers with findings:", ""]
|
||||||
total_all = 0
|
|
||||||
for image, container, total, fixable, unfixable in rows:
|
for image, container, total, fixable, unfixable in rows:
|
||||||
total_all += total
|
|
||||||
label = f"{image} [{container}]" if container else image
|
label = f"{image} [{container}]" if container else image
|
||||||
lines.append(
|
lines.append(f"* {label}: {fixable}/{total} (fixable/total)")
|
||||||
f"* {label}: {total} total "
|
lines += ["", f"Total: {sum(row[2] for row in rows)} CVE(s) in {len(rows)} container(s)"]
|
||||||
f"({fixable} fixable, {unfixable} unfixable)"
|
|
||||||
)
|
|
||||||
lines += ["", f"Total: {total_all} CVE(s) in {len(rows)} container(s)"]
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -475,7 +475,8 @@ class ReportTests(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
self.assertIn("Image: nginx:1.27", body)
|
self.assertIn("Image: nginx:1.27", body)
|
||||||
self.assertIn("Container: web", 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-1", body)
|
||||||
self.assertIn("CVE-2", body)
|
self.assertIn("CVE-2", body)
|
||||||
self.assertIn("CVE-5", body)
|
self.assertIn("CVE-5", body)
|
||||||
@@ -491,7 +492,8 @@ class ReportTests(unittest.TestCase):
|
|||||||
self.assertIn("CVE-1", body)
|
self.assertIn("CVE-1", body)
|
||||||
self.assertNotIn("CVE-2", body)
|
self.assertNotIn("CVE-2", body)
|
||||||
self.assertNotIn("CVE-5", 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:
|
def test_build_other_containers_body_has_counts_per_container(self) -> None:
|
||||||
body = scan.build_other_containers_body(
|
body = scan.build_other_containers_body(
|
||||||
@@ -500,8 +502,8 @@ class ReportTests(unittest.TestCase):
|
|||||||
("redis:7", "cache", 4, 0, 4),
|
("redis:7", "cache", 4, 0, 4),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
self.assertIn("* nginx:1.27 [web-worker]: 3 total (2 fixable, 1 unfixable)", body)
|
self.assertIn("* nginx:1.27 [web-worker]: 2/3 (fixable/total)", body)
|
||||||
self.assertIn("* redis:7 [cache]: 4 total (0 fixable, 4 unfixable)", body)
|
self.assertIn("* redis:7 [cache]: 0/4 (fixable/total)", body)
|
||||||
self.assertIn("Total: 7 CVE(s) in 2 container(s)", body)
|
self.assertIn("Total: 7 CVE(s) in 2 container(s)", body)
|
||||||
|
|
||||||
def test_build_missing_body_separates_missing_and_unscanned(self) -> None:
|
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("CVE-2026-0001", notification)
|
||||||
self.assertIn("Vulnerability scan: other containers", notification)
|
self.assertIn("Vulnerability scan: other containers", notification)
|
||||||
self.assertIn("redis:7 [cache]", 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.
|
# The important container must not also appear in the summary.
|
||||||
self.assertNotIn("testphp-php [web]", notification)
|
self.assertNotIn("testphp-php [web]", notification)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user