diff --git a/provider/src/pacta_provider/service.py b/provider/src/pacta_provider/service.py index 03474e0..a93d12d 100644 --- a/provider/src/pacta_provider/service.py +++ b/provider/src/pacta_provider/service.py @@ -109,6 +109,18 @@ def build_attestation( "axiom_log_path": axiom.log_path if axiom else None, "axiom_diagnostics": axiom.diagnostics if axiom else [], }, + # Scope block: the human-readable honesty carried BY THE LEAF + # itself (review round 6). Previously the profile's + # guarantees/exclusions/deployment_constraints reached only the + # claim card, never the published leaf — so a leaf could not + # carry its own scoped-claim wording (the required entry-13 + # attestation-scope text lives in known_status → + # deployment_constraints). Pure text; safe to publish. + "scope": { + "guarantees": list(profile.guarantees), + "exclusions": list(profile.exclusions), + "deployment_constraints": list(profile.deployment_constraints), + }, "certificates": certs, } signed = sign_attestation(unsigned, private_key, public_key) diff --git a/tests/test_provider.py b/tests/test_provider.py index 1e3849b..66e5ad1 100644 --- a/tests/test_provider.py +++ b/tests/test_provider.py @@ -49,3 +49,42 @@ def test_provider_builds_signed_attestation_for_fixture(tmp_path): ) assert attestation["signature"]["status"] == "signed" assert attestation["certificates"][0]["status"] == "proven" + # The leaf carries its own scope block (review round 6): the + # profile's guarantees/exclusions/deployment_constraints must reach + # the published leaf, not only the claim card. + assert "scope" in attestation + for key in ("guarantees", "exclusions", "deployment_constraints"): + assert key in attestation["scope"] + + +def test_attestation_scope_carries_repo_known_status_and_exclusions(tmp_path): + # A repo's known_status (scoped-claim wording) and known_exclusions + # must land in the leaf's scope block. Regression for the entry-13 + # requirement that the leaf itself carry its scoped attestation text. + private_key = tmp_path / "provider.key" + public_key = tmp_path / "provider.pub" + generate_ed25519_keypair(private_key, public_key) + repo = RepoConfig( + name="dalek-ed25519-verified", + url="https://github.com/saymrwulf/dalek-ed25519-verified.git", + kind="ed25519", + verification_dir="verification", + verified_backend="serial/u64", + certificates=["CurveFieldProofs.fieldImplementation"], + axiom_imports=["Proofs.FieldMain"], + expected_axioms=[], + known_status="SCOPE MARKER: mechanized model only, not the deployed verifier.", + known_exclusions=["EXCLUSION MARKER: side-channel resistance"], + ) + attestation = build_attestation( + repo, + Path("tests/fixtures/mini-ed25519-verified"), + provider="local-test-provider", + private_key=private_key, + public_key=public_key, + timeout=30, + log_dir=tmp_path / "logs", + ) + scope = attestation["scope"] + assert any("SCOPE MARKER" in c for c in scope["deployment_constraints"]) + assert any("EXCLUSION MARKER" in e for e in scope["exclusions"])