Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tests/conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,6 @@ Spec: [trace-a2a-profile.md](../../docs/spec/trace-a2a-profile.md), [provenance-
| ACTION-006 | MUST | A valid delegated action denied by local policy is classified as authorization-invalid, not malformed provenance. | `SCOPE_NOT_PERMITTED`. |
| ACTION-007 | MUST | A valid delegated action whose controller outcome is negative remains valid evidence of a negative outcome. | `valid_negative_outcome`. |
| ACTION-008 | MUST | An action whose delegation chain contains a credential with an invalid signature is rejected as provenance-invalid before authorization or outcome handling. | `INVALID_CREDENTIAL`. |
| ACTION-009 | MUST | A delegated action with a strictly attenuating multi-hop credential chain verifies. | `verified`. |
| ACTION-010 | MUST | An action evidence chain with scope widening at an intermediate hop is rejected as provenance-invalid. | `SCOPE_ESCALATION`. |
| ACTION-011 | MUST | Action evidence whose delegatee differs from the subject of the referenced credential is rejected as provenance-invalid. | `PROVENANCE_LINK_BROKEN`. |
54 changes: 54 additions & 0 deletions tests/conformance/test_profile_conformance.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,3 +435,57 @@ def test_action_008_invalid_delegation_signature_is_provenance_invalid() -> None
assert _verify_action_evidence(bad_chain, records, evidence, permissive_policy) == (
_ActionEvidenceResult("provenance_invalid", "INVALID_CREDENTIAL")
)
def test_action_009_multi_hop_attenuation_verifies() -> None:
chain = build_chain(
[
frozenset({"robot.move", "robot.inspect", "robot.stop"}),
frozenset({"robot.move", "robot.inspect"}),
frozenset({"robot.move"}),
]
)
records = _records(chain)
result = _verify_action_evidence(
chain,
records,
_action_evidence(records),
LocalPolicy.of(["robot.move", "robot.inspect"]),
)
assert result == _ActionEvidenceResult("verified", "ACCEPTED")


def test_action_010_intermediate_scope_widening_is_provenance_invalid() -> None:
chain = build_chain(
[
frozenset({"robot.move"}),
frozenset({"robot.move", "robot.inspect"}),
frozenset({"robot.move"}),
]
)
records = _records(chain)
result = _verify_action_evidence(
chain,
records,
_action_evidence(records),
LocalPolicy.of(["robot.move", "robot.inspect"]),
)
assert result == _ActionEvidenceResult("provenance_invalid", "SCOPE_ESCALATION")


def test_action_011_delegatee_mismatch_is_provenance_invalid() -> None:
chain = _action_chain()
records = _records(chain)
leaf = records[-1]
records[-1] = DelegationRecord(
leaf.record_id,
leaf.credential_id,
subject="different-delegatee",
scope=leaf.scope,
parent_record_hash=leaf.parent_record_hash,
)
result = _verify_action_evidence(
chain,
records,
_action_evidence(records),
LocalPolicy.of(["robot.move"]),
)
assert result == _ActionEvidenceResult("provenance_invalid", "PROVENANCE_LINK_BROKEN")
Loading