more changes

This commit is contained in:
David Duran Rosich committed 2026-10-28 00:38:34 +01:00
1 parent 0fe06f2ba5
commit be5d4dd97f
3 files changed
+152 -55

No files matched your search

+42 -40
View File
@@ -1000,7 +1000,9 @@ def summary_table(
"""Render image/container/severity counts as a monospaced markdown table."""
headers = ["Image", "Container"] + [name[0] for name in severities]
table_rows: list[list[str]] = []
for image, container, counts, fixable_counts in rows:
for image, container, counts, fixable_counts in sorted(
rows, key=lambda row: (row[1].casefold(), row[0].casefold())
):
cells = [short_image(image), container or "-"]
cells += [
f"{fixable_counts.get(name, 0)}/{counts.get(name, 0)}"
@@ -1061,28 +1063,6 @@ def build_important_body(
return "\n".join(lines)
def build_important_summary_body(
container: str, target: ScanTarget, result: ScanResult, min_severity: str
) -> str:
"""Summary for an important container with no findings above threshold."""
entries = cve_entries(result, severity_rank(min_severity))
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
severities = visible_severities(severity_rank(min_severity))
lines = [
summary_table(
[(target.image, container, counts, fixable_counts)], severities
),
"",
"No CVEs at or above the notification threshold.",
]
return "\n".join(lines)
def _chunk_list(items: list[object], size: int) -> list[list[object]]:
if size <= 0:
return [items]
@@ -1093,7 +1073,7 @@ def build_other_containers_bodies(
rows: list,
rank: int,
) -> list[str]:
"""Split the summary by container groups so each message stays small."""
"""Split the summary by image groups so each message stays small."""
if not rows:
return []
severities = visible_severities(rank)
@@ -1124,7 +1104,21 @@ def build_final_summary_body(
severities = visible_severities(severity_rank(min_severity))
table_rows: list[tuple[str, str, dict[str, int], dict[str, int]]] = []
package_lines: list[str] = []
# Merge important containers that share an image into one row/package line.
by_image: dict[str, tuple[list[str], ScanTarget, ScanResult]] = {}
for name, target, result in important_rows:
if target.image in by_image:
by_image[target.image][0].append(name)
else:
by_image[target.image] = ([name], target, result)
groups = sorted(
(
(", ".join(sorted(names, key=str.casefold)), target, result)
for names, target, result in by_image.values()
),
key=lambda group: (group[0].casefold(), group[1].image.casefold()),
)
for name, target, result in groups:
entries = cve_entries(result, severity_rank(min_severity))
counts = severity_counts(entries)
fixable_counts = {severity: 0 for severity in REPORTED_SEVERITIES}
@@ -1168,7 +1162,7 @@ def build_final_summary_body(
lines += [
"",
f"Overall severity: {severity_text(overall_counts, overall_fixable_counts, severities)}",
f"Overall: {overall_fixable}/{overall_total} fixable/total in {len(all_rows)} container(s)",
f"Overall: {overall_fixable}/{overall_total} fixable/total in {len(all_rows)} image(s)",
]
return "\n".join(lines)
@@ -1451,7 +1445,11 @@ def build_container_rows(
skip: set[str] | None = None,
include_empty: bool = False,
) -> list:
"""Build ``(image, container, total, fixable, unfixable, counts, fixable_counts)`` rows."""
"""Build ``(image, containers, total, fixable, unfixable, counts, fixable_counts)`` rows.
One row per image: containers that share the image are merged into a single
row so its counts are not repeated.
"""
skip = skip or set()
rows: list = []
for target in targets:
@@ -1467,12 +1465,17 @@ def build_container_rows(
for entry in entries:
if entry.fixable:
fixable_counts[entry.severity] += 1
for container in target.containers or [""]:
if container and container in skip:
if target.containers:
containers = [name for name in target.containers if name not in skip]
if not containers:
continue
rows.append(
(target.image, container, total, fixable, unfixable, counts, fixable_counts)
)
container = ", ".join(sorted(containers, key=str.casefold))
else:
container = ""
rows.append(
(target.image, container, total, fixable, unfixable, counts, fixable_counts)
)
rows.sort(key=lambda row: (row[1].casefold(), row[0].casefold()))
return rows
@@ -1514,20 +1517,19 @@ def send_notifications(
important_rows.append((name, target, result))
entries = cve_entries(result, settings.notify_rank)
report_path = os.path.join(settings.report_dir, f"{report_slug(image)}.json")
if entries:
body = build_important_body(
name, target, result, report_path, settings.notify_min_severity
)
else:
if not entries:
# No individual notification for clean containers; the final
# summary table already lists them with 0/0 totals.
log(
"DEBUG",
f"Important container {name} ({short_image(image)}) has no findings at or above "
f"{settings.notify_min_severity}",
)
body = build_important_summary_body(
name, target, result, settings.notify_min_severity
)
continue
report_path = os.path.join(settings.report_dir, f"{report_slug(image)}.json")
body = build_important_body(
name, target, result, report_path, settings.notify_min_severity
)
title = f"{settings.apprise_title_prefix}: {name} ({short_image(image)})"
log(
"DEBUG",