Files
computer-playbook/tests/unit/python/cli/contributing/security/test_summary.py
T
kevinveenbirkenbachandClaude Opus 5.5 5e5e61d255 feat(ci): scan the mirrored images daily and track their CVEs as issues
The daily mirror workflow gains a scan job after the sync. It runs
make test-security against the GHCR mirror, writes the failing findings per
image and per CVE into the job summary and uploads the report as the
vulnerability-findings artifact.

- One issue per CVE and image, labelled vulnerability, is opened, updated
  while the finding stays and closed once no engine reports it. Issues are
  only closed when the scan reported no error, so an image that failed to
  scan keeps its issues.
- workflow_dispatch and workflow_call take an issues switch; false is a dry
  run that only produces the summary and the artifact. Scheduled runs always
  sync the issues.
- Only the scan job holds issues: write.

Part of #1324

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 02:44:59 +01:00

80 lines
2.5 KiB
Python

from __future__ import annotations
import json
import tempfile
from pathlib import Path
from unittest import TestCase, main
import cli.contributing.security.summary.__main__ as summary_main
from utils.cache.files import read_text
def _row(image, cve, engine="grype"):
return {"image": image, "id": cve, "engine": engine}
REPORT = {
"findings": [
_row("docker.io/a:1", "CVE-2026-1001"),
_row("docker.io/a:1", "CVE-2026-1001", engine="trivy"),
_row("docker.io/a:1", "CVE-2026-1002"),
_row("docker.io/b:2", "CVE-2026-1001"),
],
"errors": ["docker.io/c:3: the mirror served no digest"],
}
class TestCounts(TestCase):
def test_an_image_counts_its_distinct_cves(self):
self.assertEqual(
summary_main.counts(REPORT)["per_image"]["docker.io/a:1"],
{"cves": 2, "findings": 3},
)
def test_a_cve_counts_its_distinct_images(self):
self.assertEqual(
summary_main.counts(REPORT)["per_cve"]["CVE-2026-1001"],
{"images": 2, "findings": 3},
)
def test_totals_include_the_scan_errors(self):
counts = summary_main.counts(REPORT)
self.assertEqual((counts["findings"], counts["errors"]), (4, 1))
class TestMarkdown(TestCase):
def test_the_most_affected_image_comes_first(self):
text = summary_main.markdown(summary_main.counts(REPORT))
self.assertLess(text.index("docker.io/a:1"), text.index("docker.io/b:2"))
def test_the_headline_names_the_totals(self):
text = summary_main.markdown(summary_main.counts(REPORT))
self.assertIn(
"4 failing finding(s) in 2 image(s), 2 CVE(s), 1 scan error(s).", text
)
class TestMain(TestCase):
def test_the_counts_artifact_and_the_summary_are_written(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
(root / "report.json").write_text(json.dumps(REPORT), encoding="utf-8")
summary_main.main(
[
"--report",
str(root / "report.json"),
"--counts",
str(root / "counts.json"),
"--summary",
str(root / "summary.md"),
]
)
counts = json.loads(read_text(str(root / "counts.json")))
text = read_text(str(root / "summary.md"))
self.assertEqual(counts["findings"], 4)
self.assertIn("Vulnerability scan of the mirror", text)
if __name__ == "__main__":
main()