199 lines
7.4 KiB
Python
199 lines
7.4 KiB
Python
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()
|