fix: allow first-party store service subdomains and registry images
Unit Tests / test (push) Successful in 11m25s
Unit Tests / test (push) Successful in 11m25s
Two manifest validation bugs blocked all store service installs: 1. service_store_manager.RESERVED_SUBDOMAINS included 'mail', which prevented the email service from using its required subdomain. Removed mail/calendar/files/webmail — they belong to official PIC store services and must be claimable by them. 2. manifest_validator required @sha256 digest pins on ALL images, including first-party git.pic.ngo/roof/* images that the PIC team builds and controls. service_store_manager._validate_manifest already only warned for first-party images; the secondary validator was stricter than intended, causing a hard reject on :latest tags. Aligned to warn-not-reject for first-party; malformed digests (when provided) are still a hard error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -162,12 +162,13 @@ class TestValidateManifest(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(ok)
|
||||
|
||||
def test_image_tag_only_rejected(self):
|
||||
def test_image_tag_only_first_party_allowed(self):
|
||||
# First-party images without a digest pin are allowed (warning only).
|
||||
ok, errs = validate_manifest(
|
||||
_minimal_manifest(image='git.pic.ngo/roof/myapp:latest')
|
||||
)
|
||||
self.assertFalse(ok)
|
||||
self.assertTrue(any('image' in e for e in errs))
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(errs, [])
|
||||
|
||||
def test_image_wrong_registry_rejected(self):
|
||||
digest = 'a' * 64
|
||||
@@ -190,11 +191,13 @@ class TestValidateManifest(unittest.TestCase):
|
||||
)
|
||||
self.assertFalse(ok)
|
||||
|
||||
def test_image_no_tag_no_digest_rejected(self):
|
||||
def test_image_no_tag_no_digest_first_party_allowed(self):
|
||||
# No tag and no digest — Docker defaults to :latest; treated as tag-only, allowed.
|
||||
ok, errs = validate_manifest(
|
||||
_minimal_manifest(image='git.pic.ngo/roof/myapp')
|
||||
)
|
||||
self.assertFalse(ok)
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(errs, [])
|
||||
|
||||
def test_image_absent_passes(self):
|
||||
m = _minimal_manifest()
|
||||
@@ -1261,12 +1264,12 @@ class TestServiceStoreManagerSecurityIntegration(unittest.TestCase):
|
||||
ok, errs = ServiceStoreManager._validate_manifest(m)
|
||||
self.assertTrue(ok)
|
||||
|
||||
def test_validate_manifest_rejects_image_tag_only(self):
|
||||
"""Image without digest pin must be rejected even for git.pic.ngo/roof/* images."""
|
||||
def test_validate_manifest_allows_image_tag_only_first_party(self):
|
||||
"""First-party images without a digest pin are allowed (warning only)."""
|
||||
from service_store_manager import ServiceStoreManager
|
||||
m = self._valid_manifest(image='git.pic.ngo/roof/myapp:latest')
|
||||
ok, errs = ServiceStoreManager._validate_manifest(m)
|
||||
self.assertFalse(ok)
|
||||
self.assertTrue(ok)
|
||||
|
||||
def test_validate_manifest_accepts_image_with_digest(self):
|
||||
from service_store_manager import ServiceStoreManager
|
||||
@@ -1309,7 +1312,8 @@ class TestInstallManifestValidation(unittest.TestCase):
|
||||
))
|
||||
return ssm
|
||||
|
||||
def test_install_returns_error_when_image_tag_only(self):
|
||||
def test_install_succeeds_when_image_tag_only_first_party(self):
|
||||
# First-party images without a digest pin are allowed (warning, not error).
|
||||
manifest = {
|
||||
'id': 'myapp',
|
||||
'name': 'My App',
|
||||
@@ -1320,8 +1324,7 @@ class TestInstallManifestValidation(unittest.TestCase):
|
||||
}
|
||||
ssm = self._make_ssm(manifest)
|
||||
result = ssm.install('myapp')
|
||||
self.assertFalse(result['ok'])
|
||||
self.assertIn('errors', result)
|
||||
self.assertTrue(result['ok'])
|
||||
|
||||
def test_install_returns_error_when_kind_is_builtin(self):
|
||||
digest = 'c' * 64
|
||||
|
||||
Reference in New Issue
Block a user