feat: secure build phase 2 — enforce image verification by default

All store images are now digest-pinned and cosign-signed by the publish
pipeline, so the warn-by-default training-wheels period is over: an
unsigned or undigested image must not install unless the admin
explicitly opts out. The service_composer fallback used when the config
manager is unavailable or corrupt also flips to enforce — config
corruption must fail closed rather than silently weaken verification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 14:12:58 -04:00
parent c430a392b8
commit 2ab3d2d5ac
5 changed files with 66 additions and 13 deletions
+14 -1
View File
@@ -693,7 +693,7 @@ class TestInstall(unittest.TestCase):
composer.install.assert_not_called()
def test_install_warn_allows_undigested_image(self):
"""Under warn mode (default) an undigested image still installs."""
"""Under warn mode an undigested image still installs (warn is non-default)."""
manifest = _valid_manifest(
id='myapp', container_name='cell-myapp',
image='git.pic.ngo/roof/myapp:latest',
@@ -713,6 +713,19 @@ class TestInstall(unittest.TestCase):
self.assertTrue(result['ok'])
composer.install.assert_called_once()
def test_install_enforce_default_rejects_undigested_image(self):
"""The new default mode (enforce) rejects undigested images without explicit mode set."""
manifest = _valid_manifest(
id='myapp', container_name='cell-myapp',
image='git.pic.ngo/roof/myapp:latest',
)
ssm, cm, _, composer = _make_ssm(manifest=manifest)
cm.get_image_verification_mode.return_value = 'enforce'
result = ssm.install('myapp')
self.assertFalse(result['ok'])
self.assertIn('digest', result['error'].lower())
composer.install.assert_not_called()
def test_install_without_composer_stores_record(self):
"""When service_composer=None, skip compose but still store the install record."""
manifest = _valid_manifest(id='myapp', container_name='cell-myapp')