mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-14 06:37:25 +00:00
Each CI deploy row now draws one vulnerability engine (trivy, grype or osv) as a fifth rotation axis, shown in the job name next to distro and filesystem (🔬 axis, 🔺 trivy, 🍇 grype, 🐙 osv). The deploy-time scan of sys-ctl-hlth-runtime runs that engine behind Syft and the Grype runtime pass. - A vulnerability-db job keeps one Actions cache per database and day and downloads only the missing ones. Every deploy job restores Grype plus its drawn engine, fails on a cache miss and never downloads a database itself. - Local stacks share one host directory, INFINITO_VULNERABILITY_DB_HOST_PATH, mounted read-only at INFINITO_VULNERABILITY_DB_DIR in compose.yml and in every swarm node. A local deploy without a pinned engine draws one and refreshes only the databases it reads, at most once a day, under a lock. - A pinned engine whose database is missing stops the deploy before it starts. Part of #1324 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
87 lines
2.8 KiB
Python
87 lines
2.8 KiB
Python
from __future__ import annotations
|
|
|
|
import contextlib
|
|
import io
|
|
import random
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest import TestCase, main, mock
|
|
|
|
from utils.security import deploy
|
|
from utils.security.engine import ENGINE_ENV, ROLE, databases_for
|
|
|
|
|
|
class TestInventoryOverrides(TestCase):
|
|
def setUp(self):
|
|
self.tmp = tempfile.TemporaryDirectory()
|
|
self.root = Path(self.tmp.name)
|
|
self.refreshed = []
|
|
|
|
def tearDown(self):
|
|
self.tmp.cleanup()
|
|
|
|
def _env(self, engine):
|
|
return mock.patch.dict(
|
|
"os.environ",
|
|
{
|
|
ENGINE_ENV: engine,
|
|
deploy.HOST_PATH_ENV: str(self.root),
|
|
deploy.TARGET_DIR_ENV: "/opt/vulnerability-db",
|
|
},
|
|
)
|
|
|
|
def _refresher(self, errors):
|
|
def refresher(root, engines):
|
|
self.refreshed.append((root, engines))
|
|
return errors
|
|
|
|
return refresher
|
|
|
|
def _overrides(self, engine, errors=()):
|
|
with self._env(engine), contextlib.redirect_stdout(io.StringIO()):
|
|
return deploy.inventory_overrides(
|
|
random.Random(4), # noqa: S311 - seeded draw, not crypto
|
|
self._refresher(list(errors)),
|
|
)
|
|
|
|
def _runtime(self, overrides):
|
|
return overrides[ROLE]["services"]["runtime"]
|
|
|
|
def test_a_local_deploy_draws_one_engine_behind_syft(self):
|
|
engines = self._runtime(self._overrides(""))["engines"]
|
|
self.assertEqual((len(engines), engines[0]), (2, "syft"))
|
|
|
|
def test_a_local_deploy_refreshes_the_shared_databases_it_reads(self):
|
|
engine = self._runtime(self._overrides(""))["engines"][1]
|
|
self.assertEqual(self.refreshed, [(self.root, databases_for(engine))])
|
|
|
|
def test_the_stack_reads_the_read_only_mount(self):
|
|
runtime = self._runtime(self._overrides(""))
|
|
self.assertEqual(
|
|
(runtime["database_dir"], runtime["db_update"]),
|
|
("/opt/vulnerability-db", False),
|
|
)
|
|
|
|
def test_a_failed_refresh_fails_the_deploy(self):
|
|
with self.assertRaisesRegex(SystemExit, "no network"):
|
|
self._overrides("", errors=["grype database: no network"])
|
|
|
|
def test_a_pinned_engine_never_downloads(self):
|
|
for name in ("trivy", "grype"):
|
|
(self.root / name).mkdir()
|
|
(self.root / name / "db").write_text("x", encoding="utf-8")
|
|
self.assertEqual(
|
|
self._runtime(self._overrides("trivy"))["engines"], ["syft", "trivy"]
|
|
)
|
|
self.assertEqual(self.refreshed, [])
|
|
|
|
def test_a_pinned_engine_without_its_databases_fails(self):
|
|
(self.root / "grype").mkdir()
|
|
(self.root / "grype" / "db").write_text("x", encoding="utf-8")
|
|
with self.assertRaisesRegex(SystemExit, "no osv database"):
|
|
self._overrides("osv")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|