feat: enforce signed target maturity evidence
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
from datetime import UTC, datetime
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
||||
|
||||
|
||||
META_ROOT = Path(__file__).resolve().parents[1]
|
||||
ASSESSMENT_TOOLS_ROOT = META_ROOT / "tools" / "assessments"
|
||||
RELEASE_TOOLS_ROOT = META_ROOT / "tools" / "release"
|
||||
for tools_root in (ASSESSMENT_TOOLS_ROOT, RELEASE_TOOLS_ROOT):
|
||||
if str(tools_root) not in sys.path:
|
||||
sys.path.insert(0, str(tools_root))
|
||||
|
||||
from govoplan_assessment.boundary_evidence import ( # noqa: E402
|
||||
issue_boundary_evidence,
|
||||
)
|
||||
from govoplan_assessment.evidence import ( # noqa: E402
|
||||
InstalledEvidenceReview,
|
||||
canonical_sha256,
|
||||
review_boundary_evidence,
|
||||
validate_payload,
|
||||
)
|
||||
|
||||
|
||||
class BoundaryEvidenceIssuerTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.assessment = {
|
||||
"assessment_id": "assessment:test",
|
||||
"release": {"ref": "stable-catalog-202608020001"},
|
||||
"deployment_profile": {"id": "deployment:target"},
|
||||
}
|
||||
self.installed = {
|
||||
"schema_version": "0.1.0",
|
||||
"assessment_id": "assessment:test",
|
||||
"artifacts": [],
|
||||
}
|
||||
self.private_key = Ed25519PrivateKey.generate()
|
||||
public_key = base64.b64encode(
|
||||
self.private_key.public_key().public_bytes(
|
||||
encoding=serialization.Encoding.Raw,
|
||||
format=serialization.PublicFormat.Raw,
|
||||
)
|
||||
).decode("ascii")
|
||||
self.authority = {
|
||||
"$schema": "./capability-fit-proof-authority-keyring.schema.json",
|
||||
"schema_version": "0.1.0",
|
||||
"purpose": "govoplan.capability-fit-proof-authorities",
|
||||
"keys": [
|
||||
{
|
||||
"key_id": "authority:target",
|
||||
"status": "active",
|
||||
"public_key": public_key,
|
||||
"allowed_scopes": ["target_environment", "recovery"],
|
||||
"not_before": "2026-08-01T00:00:00Z",
|
||||
"not_after": "2026-09-01T00:00:00Z",
|
||||
}
|
||||
],
|
||||
}
|
||||
self.boundary_schema = json.loads(
|
||||
(
|
||||
META_ROOT / "docs" / "capability-fit-boundary-evidence.schema.json"
|
||||
).read_text(encoding="utf-8")
|
||||
)
|
||||
self.authority_schema = json.loads(
|
||||
(
|
||||
META_ROOT
|
||||
/ "docs"
|
||||
/ "capability-fit-proof-authority-keyring.schema.json"
|
||||
).read_text(encoding="utf-8")
|
||||
)
|
||||
|
||||
def test_issues_sanitized_hash_bound_proof_and_verifies_it(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
result_path = Path(temp_dir) / "recovery-result.json"
|
||||
result_path.write_text(
|
||||
'{"rto_seconds":42,"rpo_seconds":0,"reconstructed":true}\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
proof = self.issue(
|
||||
claims=[
|
||||
{
|
||||
"scope": "target_environment",
|
||||
"result": "passed",
|
||||
"control_ids": ["topology:shared-state-v1"],
|
||||
"artifacts": [
|
||||
{"artifact_id": "target:run-1", "path": result_path}
|
||||
],
|
||||
},
|
||||
{
|
||||
"scope": "recovery",
|
||||
"result": "passed",
|
||||
"control_ids": ["recovery:restore-v1"],
|
||||
"artifacts": [
|
||||
{"artifact_id": "recovery:run-1", "path": result_path}
|
||||
],
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
(), validate_payload(payload=proof, schema=self.boundary_schema)
|
||||
)
|
||||
self.assertNotIn(str(result_path), json.dumps(proof))
|
||||
self.assertEqual(
|
||||
canonical_sha256(self.installed), proof["installed_evidence_sha256"]
|
||||
)
|
||||
installed_review = InstalledEvidenceReview(
|
||||
findings=(),
|
||||
changes=(),
|
||||
changed_repositories=frozenset(),
|
||||
affected_module_ids=frozenset(),
|
||||
proof_scope={
|
||||
"installed_artifacts": {"valid": True},
|
||||
"installed_release_origin": {"valid": True},
|
||||
},
|
||||
evidence_sha256=canonical_sha256(self.installed),
|
||||
)
|
||||
review = review_boundary_evidence(
|
||||
assessment=self.assessment,
|
||||
installed_review=installed_review,
|
||||
evidence=proof,
|
||||
evidence_schema=self.boundary_schema,
|
||||
authority_keyring=self.authority,
|
||||
authority_keyring_schema=self.authority_schema,
|
||||
verification_time=datetime(2026, 8, 2, 12, 30, tzinfo=UTC),
|
||||
)
|
||||
|
||||
self.assertEqual((), review.findings)
|
||||
self.assertTrue(review.proof_scope["target_environment"]["valid"])
|
||||
self.assertTrue(review.proof_scope["recovery"]["valid"])
|
||||
|
||||
def test_refuses_missing_release_origin_or_scope_authority(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
artifact = Path(temp_dir) / "result.txt"
|
||||
artifact.write_text("passed\n", encoding="utf-8")
|
||||
claim = {
|
||||
"scope": "security",
|
||||
"result": "passed",
|
||||
"control_ids": ["security:sast-v1"],
|
||||
"artifacts": [{"artifact_id": "security:run-1", "path": artifact}],
|
||||
}
|
||||
with self.assertRaisesRegex(ValueError, "release origin"):
|
||||
self.issue(claims=[claim], release_origin_verified=False)
|
||||
with self.assertRaisesRegex(ValueError, "authorized for scopes: security"):
|
||||
self.issue(claims=[claim])
|
||||
|
||||
def test_refuses_authority_that_expires_before_the_proof(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
artifact = Path(temp_dir) / "result.txt"
|
||||
artifact.write_text("passed\n", encoding="utf-8")
|
||||
with self.assertRaisesRegex(ValueError, "full proof interval"):
|
||||
self.issue(
|
||||
expires_at=datetime(2026, 10, 1, tzinfo=UTC),
|
||||
claims=[
|
||||
{
|
||||
"scope": "recovery",
|
||||
"result": "passed",
|
||||
"control_ids": ["recovery:restore-v1"],
|
||||
"artifacts": [
|
||||
{
|
||||
"artifact_id": "recovery:run-1",
|
||||
"path": artifact,
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
def issue(
|
||||
self,
|
||||
*,
|
||||
claims: list[dict[str, object]],
|
||||
expires_at: datetime = datetime(2026, 8, 3, tzinfo=UTC),
|
||||
release_origin_verified: bool = True,
|
||||
) -> dict[str, object]:
|
||||
return issue_boundary_evidence(
|
||||
assessment=self.assessment,
|
||||
installed_evidence=self.installed,
|
||||
proof_id="proof:target:1",
|
||||
claims=claims,
|
||||
authority_keyring=self.authority,
|
||||
signing_keys={"authority:target": self.private_key},
|
||||
issued_at=datetime(2026, 8, 2, 12, tzinfo=UTC),
|
||||
expires_at=expires_at,
|
||||
release_origin_verified=release_origin_verified,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -26,6 +26,7 @@ for tools_root in (ASSESSMENT_TOOLS_ROOT, RELEASE_TOOLS_ROOT):
|
||||
sys.path.insert(0, str(tools_root))
|
||||
|
||||
from govoplan_assessment.capability_fit import ( # noqa: E402
|
||||
enforce_required_boundary_scopes,
|
||||
local_tag_provenance,
|
||||
render_review,
|
||||
review_capability_fit,
|
||||
@@ -74,6 +75,34 @@ class CapabilityFitReviewTests(unittest.TestCase):
|
||||
json.dumps(report, sort_keys=True), json.dumps(repeated, sort_keys=True)
|
||||
)
|
||||
|
||||
def test_required_boundary_scope_blocks_admission_until_positive(self) -> None:
|
||||
catalog, keyring = signed_catalog(self.assessment)
|
||||
report = review_capability_fit(
|
||||
assessment=deepcopy(self.assessment),
|
||||
schema=self.schema,
|
||||
catalog=catalog,
|
||||
published_keyring=keyring,
|
||||
trusted_keyring=keyring,
|
||||
)
|
||||
|
||||
enforce_required_boundary_scopes(
|
||||
report, required_scopes=("security", "recovery")
|
||||
)
|
||||
|
||||
self.assertEqual("blocked", report["status"])
|
||||
self.assertFalse(report["proof_scope"]["admission"]["valid"])
|
||||
self.assertEqual(
|
||||
["recovery", "security"],
|
||||
report["proof_scope"]["admission"]["failed_scopes"],
|
||||
)
|
||||
self.assertEqual(
|
||||
2,
|
||||
sum(
|
||||
item["code"] == "required_boundary_scope_unsatisfied"
|
||||
for item in report["findings"]
|
||||
),
|
||||
)
|
||||
|
||||
def test_release_drift_identifies_affected_conclusions(self) -> None:
|
||||
catalog, keyring = signed_catalog(
|
||||
self.assessment, versions={"campaigns": "0.1.11"}, sequence=202607230001
|
||||
@@ -360,7 +389,7 @@ class CapabilityFitReviewTests(unittest.TestCase):
|
||||
|
||||
self.assertIn("Capability fit rerun: current", rendered)
|
||||
self.assertIn(
|
||||
"No installed-composition, installed-release-origin, target-environment, external-provider, production-approval proof",
|
||||
"No installed-composition, installed-release-origin, target-environment, external-providers, accessibility, privacy, security, operations, recovery, production-approval proof",
|
||||
rendered,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user