mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-14 14:47:12 +00:00
The runtime check judged PHP by its upstream version and failed every container on an end-of-life line. Distribution builds backport fixes without raising that number, so main run 41154895895 failed Checkmk#0 on `PHP 8.1.2-1ubuntu2.28 runs the end-of-life line 8.1`, a build Ubuntu lists as fixed for CVE-2026-109618 and CVE-2026-109619. - The role now runs Trivy, Grype and OSV-Scanner, with a Syft SBOM per target, against the host and the images of the running containers, in bounded parallel lanes. Targets, engines and the failing severities are lists in meta/services.yml; the default is every target and engine at CRITICAL and HIGH with a fix available. - Grype always scans the binary-classified packages of the SBOM, so a PHP built from source in an official php image (8.4.26) still fails on the CVEs fixed in 8.4.27, which Trivy and OSV do not see. - OSV reads an SBOM copy whose distro purls carry the source package name. - A CVE is suppressed only in the role owning the image, under services.<service>.security.suppressions with id, until and a `# nocheck: cve <reason>` marker; a lint rejects bare, malformed and expired entries. - The deploy-time run fails on its first finding: sys-service gains system_service_run_once and sys-service-terminator skips the retries only for those units. The timer runs daily at 00:00 UTC. - The PHP version floors are removed; the php_hardening check stays. - make test-security replaces make security-runtimes: Syft builds an SBOM of every pinned image straight from the GHCR mirror, cached by digest, and the three engines scan it with the role's threshold and suppressions. Part of #1324 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
73 lines
2.4 KiB
Python
73 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
import random
|
|
from unittest import TestCase, main
|
|
|
|
from utils.security import engine
|
|
from utils.security.pipeline import pipeline
|
|
|
|
|
|
class TestEnginesFor(TestCase):
|
|
def test_every_pin_runs_behind_syft(self):
|
|
for name in engine.SCANNERS:
|
|
with self.subTest(engine=name):
|
|
self.assertEqual(engine.engines_for(name), ["syft", name])
|
|
|
|
def test_an_unknown_engine_is_rejected(self):
|
|
with self.assertRaises(ValueError):
|
|
engine.engines_for("clair")
|
|
|
|
def test_every_pin_reads_the_grype_database(self):
|
|
self.assertEqual(engine.databases_for("trivy"), ["trivy", "grype"])
|
|
self.assertEqual(engine.databases_for("grype"), ["grype"])
|
|
|
|
def test_the_ci_pool_matches_the_role_pipeline(self):
|
|
self.assertEqual(engine.SCANNERS, pipeline().SCANNERS)
|
|
|
|
def test_every_pin_is_a_valid_role_config(self):
|
|
p = pipeline()
|
|
for name in engine.SCANNERS:
|
|
with self.subTest(engine=name):
|
|
config = p.scan_config(
|
|
{
|
|
"targets": ["host", "container"],
|
|
"engines": engine.engines_for(name),
|
|
"severity": ["CRITICAL", "HIGH"],
|
|
"fixed_only": True,
|
|
"workers": 2,
|
|
"db_update": False,
|
|
}
|
|
)
|
|
self.assertEqual(list(config.databases()), engine.databases_for(name))
|
|
|
|
|
|
class TestDrawEngine(TestCase):
|
|
def test_a_draw_is_one_scanner(self):
|
|
rng = random.Random(1) # noqa: S311 - seeded draw, not crypto
|
|
self.assertIn(engine.draw_engine(rng), engine.SCANNERS)
|
|
|
|
def test_draws_cover_every_scanner(self):
|
|
rng = random.Random(7) # noqa: S311 - seeded draw, not crypto
|
|
self.assertEqual(
|
|
{engine.draw_engine(rng) for _ in range(60)}, set(engine.SCANNERS)
|
|
)
|
|
|
|
|
|
class TestDeployOverrides(TestCase):
|
|
def test_a_deploy_reads_provided_databases(self):
|
|
runtime = engine.deploy_overrides("osv", "/opt/vulnerability-db")[engine.ROLE][
|
|
"services"
|
|
]["runtime"]
|
|
self.assertEqual(
|
|
runtime,
|
|
{
|
|
"engines": ["syft", "osv"],
|
|
"database_dir": "/opt/vulnerability-db",
|
|
"db_update": False,
|
|
},
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|