From 1e6a9c43d9706ccbe12015d94fab3ce136438eeb Mon Sep 17 00:00:00 2001 From: lywinged <48041247+lywinged@users.noreply.github.com> Date: Wed, 12 Aug 2026 17:28:44 +0000 Subject: [PATCH 1/2] fix(schema): resync the packaged copy from the normative schema The packaged schemas/trace-claim.json was a v0.1 body under a v0.2 label: 13 top-level properties, no delegation block, no provenance_depth. Records signed against the normative schema were rejected by the packaged one. Since this branch was first written the gap widened - the normative source gained the delegation link block and the provenance depth field (trace-spec #173) - so the resync now carries those too. Packaged copy is byte-identical to schema/trace-claim.json at trace-spec 1c5a9b9. The hand-written enforcement enums gain 'declared' to match, which keeps the #62 parity guard green on the moved schema rather than green on the stale one. 183 passed, 5 xpassed (the TEE-hardware smoke set, strict=False by design, unrelated). Signed-off-by: lywinged <48041247+lywinged@users.noreply.github.com> --- schemas/trace-claim.json | 587 +++++++++++++++++++++++------- src/trace_tests/modules/tr_pol.py | 2 +- tests/test_level0.py | 2 +- 3 files changed, 448 insertions(+), 143 deletions(-) diff --git a/schemas/trace-claim.json b/schemas/trace-claim.json index 73f8805..97954c2 100644 --- a/schemas/trace-claim.json +++ b/schemas/trace-claim.json @@ -1,141 +1,446 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://agentrust-io.com/schema/trace-v0.1.json", - "title": "TRACE Trust Record", - "description": "A TRACE v0.2 Trust Record — hardware-attested governance evidence for an AI agent execution.", - "type": "object", - "required": [ - "eat_profile", "iat", "subject", "model", "runtime", "policy", - "data_class", "build_provenance", "appraisal", "cnf" - ], - "properties": { - "eat_profile": {"type": "string", "const": "tag:agentrust-io.com,2026:trace-v0.2"}, - "iat": {"type": "integer", "minimum": 1700000000}, - "subject": {"type": "string", "pattern": "^(spiffe://|did:)"}, - "model": { - "type": "object", - "required": ["provider", "model_id"], - "properties": { - "provider": {"type": "string"}, - "model_id": {"type": "string"}, - "version": {"type": "string"}, - "weights_digest": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "aibom_uri": {"type": "string", "format": "uri"} - }, - "additionalProperties": false - }, - "runtime": { - "type": "object", - "required": ["platform", "measurement"], - "properties": { - "platform": {"type": "string", "enum": ["intel-tdx", "amd-sev-snp", "azure-cvm-sev-snp", "nvidia-h100", "nvidia-blackwell", "aws-nitro", "arm-cca", "google-confidential-space", "tpm2", "software-only"]}, - "measurement": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "rim_uri": {"type": "string", "format": "uri"}, - "nonce": {"type": "string"}, - "firmware_version": {"type": "string"} - }, - "additionalProperties": false - }, - "policy": { - "type": "object", - "required": ["bundle_hash", "enforcement_mode"], - "properties": { - "bundle_hash": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "enforcement_mode": {"type": "string", "enum": ["enforce", "advisory", "silent"]}, - "version": {"type": "string"}, - "policy_uri": {"type": "string", "format": "uri"} - }, - "additionalProperties": false - }, - "data_class": {"type": "string"}, - "tool_transcript": { - "type": "object", - "required": ["hash"], - "properties": { - "hash": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "call_count": {"type": "integer", "minimum": 0}, - "transcript_uri": {"type": "string", "format": "uri"} - }, - "additionalProperties": false - }, - "origin": { - "type": "object", - "description": "Where imported evidence came from. A non-self origin cannot carry a hardware runtime platform.", - "required": ["kind", "producer"], - "properties": { - "kind": { - "type": "string", - "enum": ["self", "third-party-control-plane", "log-import"] - }, - "producer": {"type": "string", "minLength": 1}, - "source_event_id": {"type": "string", "minLength": 1}, - "ingested_at": {"type": "integer", "minimum": 1700000000} - }, - "additionalProperties": false - }, - "build_provenance": { - "type": "object", - "required": ["slsa_level", "digest"], - "properties": { - "slsa_level": {"type": "integer", "minimum": 0, "maximum": 3}, - "builder": {"type": "string"}, - "digest": {"type": "string", "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"}, - "provenance_uri": {"type": "string", "format": "uri"} - }, - "additionalProperties": false - }, - "appraisal": { - "type": "object", - "required": ["status", "verifier"], - "properties": { - "status": {"type": "string", "enum": ["affirming", "warning", "contraindicated", "none"]}, - "verifier": {"type": "string", "format": "uri"}, - "policy_ref": {"type": "string", "format": "uri"}, - "timestamp": {"type": "integer"} - }, - "additionalProperties": false - }, - "transparency": {"type": "string", "format": "uri"}, - "cnf": { - "type": "object", - "required": ["jwk"], - "properties": { - "jwk": { - "type": "object", - "required": ["kty"], - "properties": { - "kty": {"type": "string"}, - "crv": {"type": "string"}, - "x": {"type": "string"}, - "y": {"type": "string"}, - "kid": {"type": "string"} - } - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false, - "allOf": [ - { - "if": { - "required": ["origin"], - "properties": { - "origin": { - "required": ["kind"], - "properties": { - "kind": {"enum": ["third-party-control-plane", "log-import"]} - } - } - } - }, - "then": { - "properties": { - "runtime": { - "properties": {"platform": {"const": "software-only"}} - } - } - } - } - ] -} +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://agentrust-io.com/schema/trace-v0.2.json", + "title": "TRACE Trust Record", + "description": "A TRACE v0.2 Trust Record — hardware-attested governance evidence for an AI agent execution.", + "type": "object", + "required": [ + "eat_profile", + "iat", + "subject", + "model", + "runtime", + "policy", + "data_class", + "build_provenance", + "appraisal", + "cnf" + ], + "properties": { + "eat_profile": { + "type": "string", + "const": "tag:agentrust-io.com,2026:trace-v0.2", + "description": "EAT profile URI identifying this as a TRACE v0.2 Trust Record." + }, + "iat": { + "type": "integer", + "description": "Issued-at time as Unix epoch seconds.", + "minimum": 1700000000 + }, + "subject": { + "type": "string", + "description": "Workload identity as a SPIFFE SVID URI or DID URI.", + "pattern": "^(spiffe://|did:)" + }, + "model": { + "type": "object", + "description": "Model identity and provenance.", + "required": [ + "provider", + "model_id" + ], + "properties": { + "provider": { + "type": "string", + "description": "Model provider (e.g. 'anthropic', 'openai', 'meta')." + }, + "model_id": { + "type": "string", + "description": "Model identifier as used by the provider." + }, + "version": { + "type": "string", + "description": "Model version or snapshot identifier." + }, + "weights_digest": { + "type": "string", + "description": "SHA-256 or SHA-384 digest of the model weights. Required for local/confidential-inference deployments.", + "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$" + }, + "aibom_uri": { + "type": "string", + "format": "uri", + "description": "URI to SPDX 3.0 AI Profile or CycloneDX 1.7 ML-BOM for this model." + } + }, + "additionalProperties": false + }, + "runtime": { + "type": "object", + "description": "TEE measurement chain binding the workload to hardware.", + "required": [ + "platform", + "measurement" + ], + "properties": { + "platform": { + "type": "string", + "enum": [ + "intel-tdx", + "amd-sev-snp", + "azure-cvm-sev-snp", + "nvidia-h100", + "nvidia-blackwell", + "aws-nitro", + "arm-cca", + "google-confidential-space", + "tpm2", + "software-only" + ], + "description": "Hardware platform providing the root of trust. software-only marks development-mode records with no hardware backing; they must never be treated as attested evidence." + }, + "measurement": { + "type": "string", + "description": "Hardware measurement of the workload (e.g. TDX MRTD, SEV measurement, TPM PCR composite).", + "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$" + }, + "rim_uri": { + "type": "string", + "format": "uri", + "description": "URI to the vendor-published Reference Integrity Manifest for this measurement." + }, + "nonce": { + "type": "string", + "description": "Freshness nonce binding the attestation report to this record (base64url, no padding)." + }, + "firmware_version": { + "type": "string", + "description": "Firmware or microcode version included in the measurement." + } + }, + "additionalProperties": false + }, + "policy": { + "type": "object", + "description": "Policy bundle sealed to the TEE measurement.", + "required": [ + "bundle_hash", + "enforcement_mode" + ], + "properties": { + "bundle_hash": { + "type": "string", + "description": "SHA-256 or SHA-384 digest of the policy bundle in force at execution time.", + "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$" + }, + "enforcement_mode": { + "type": "string", + "enum": [ + "enforce", + "advisory", + "silent", + "declared" + ], + "default": "enforce", + "description": "How the policy named by bundle_hash related to this execution. enforce: evaluated and blocked on deny. advisory: evaluated, logged, allowed. silent: evaluated and enforced with operational logs suppressed; the audit chain still records every would-have-denied decision. declared: the policy is named and bound into the signed record and NOTHING evaluated it, which is the honest value for a producer with no policy engine, such as an agent framework. Gateways MUST default to enforce. A deployment MUST explicitly configure silent. declared is never a default, a producer that evaluates policy MUST NOT use it, and a consumer MUST NOT read it as evidence that any rule was checked." + }, + "version": { + "type": "string", + "description": "Policy bundle version (semantic versioning recommended)." + }, + "policy_uri": { + "type": "string", + "format": "uri", + "description": "URI to the policy bundle for verification." + } + }, + "additionalProperties": false + }, + "data_class": { + "type": "string", + "description": "Highest-sensitivity data classification of inputs and outputs processed during this execution.", + "examples": [ + "public", + "internal", + "confidential", + "restricted", + "top-secret" + ] + }, + "tool_transcript": { + "type": "object", + "description": "Bound hash of the MCP/A2A tool-call transcript. OPTIONAL for Phase 1 records; REQUIRED for Phase 2+.", + "required": [ + "hash" + ], + "properties": { + "hash": { + "type": "string", + "description": "SHA-256 or SHA-384 digest of the full tool-call transcript, bound into the EAT envelope.", + "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$" + }, + "call_count": { + "type": "integer", + "minimum": 0, + "description": "Total number of tool calls in this session." + }, + "transcript_uri": { + "type": "string", + "format": "uri", + "description": "URI to the full transcript on the transparency log." + } + }, + "additionalProperties": false + }, + "delegation": { + "type": "object", + "description": "A2A profile: links this record to the delegating hop's Trust Record. Present when this execution acted on delegated authority; absent on a root (non-delegated) execution. A chain of these forms an offline-verifiable delegation DAG.", + "required": [ + "parent_record_hash", + "credential_id" + ], + "properties": { + "parent_record_hash": { + "type": "string", + "description": "SHA-256 or SHA-384 digest of the parent hop's Trust Record.", + "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$" + }, + "credential_id": { + "type": "string", + "minLength": 1, + "description": "Identifier of the delegation credential this hop acted under." + } + }, + "additionalProperties": false + }, + "origin": { + "type": "object", + "description": "Where the evidence in this record came from, when that is not this runtime. Absent means the runtime produced its own record, which is what a consumer assumes and what every hardware profile is. Present means something else assembled the record from evidence it did not measure. A non-self kind cannot carry a hardware runtime.platform.", + "required": [ + "kind", + "producer" + ], + "properties": { + "kind": { + "type": "string", + "enum": [ + "self", + "third-party-control-plane", + "log-import" + ], + "description": "self: the runtime produced its own record. third-party-control-plane: assembled from another vendor's runtime governance output, asserted by the system that produced it with no root outside that system. log-import: assembled from a log or export whose producer is not a control plane." + }, + "producer": { + "type": "string", + "minLength": 1, + "description": "Identifier of the system that produced the source evidence." + }, + "source_event_id": { + "type": "string", + "minLength": 1, + "description": "Identifier of the source event in the producing system, so a record can be traced back to it." + }, + "ingested_at": { + "type": "integer", + "minimum": 1700000000, + "description": "Unix time at which the source evidence was ingested. Distinct from iat, which is when this record was issued." + } + }, + "additionalProperties": false + }, + "build_provenance": { + "type": "object", + "description": "SLSA provenance for the workload (agent code + container image).", + "required": [ + "slsa_level", + "digest" + ], + "properties": { + "slsa_level": { + "type": "integer", + "minimum": 0, + "maximum": 3, + "description": "SLSA Build Level achieved. Level 0 = software-only (development/staging); Level 2 minimum for TRACE conformance; Level 3 for production mark." + }, + "builder": { + "type": "string", + "description": "SLSA builder URI." + }, + "digest": { + "type": "string", + "description": "SHA-256 or SHA-384 digest of the container image or workload binary.", + "pattern": "^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$" + }, + "provenance_uri": { + "type": "string", + "format": "uri", + "description": "URI to the SLSA provenance attestation on a Sigstore/Rekor or compatible log." + }, + "provenance_depth": { + "type": "string", + "enum": [ + "surface", + "builder", + "transitive" + ], + "description": "Depth of supply-chain verification the issuer claims for this record. surface: digest matches the workload artifact and builder is in a trusted set. builder: all of surface, plus provenance_uri resolves, the SLSA attestation signature verifies, its subject matches digest, and its builder.id matches builder. transitive: all of builder, plus every entry in the SLSA materials / resolvedDependencies has a verifiable publisher attestation. Records omitting this field are treated as surface by verifiers, so existing records keep their meaning." + } + }, + "additionalProperties": false + }, + "appraisal": { + "type": "object", + "description": "Verifier's EAR appraisal of the evidence (draft-ietf-rats-ar4si).", + "required": [ + "status", + "verifier" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "affirming", + "warning", + "contraindicated", + "none" + ], + "description": "EAR appraisal status." + }, + "verifier": { + "type": "string", + "format": "uri", + "description": "URI identifying the verifier that produced this appraisal." + }, + "policy_ref": { + "type": "string", + "format": "uri", + "description": "URI to the appraisal policy used." + }, + "timestamp": { + "type": "integer", + "description": "Unix epoch seconds when the appraisal was produced." + }, + "provenance_depth_verified": { + "type": "string", + "enum": [ + "surface", + "builder", + "transitive" + ], + "description": "Depth at which this verifier actually checked build_provenance, which is not necessarily the depth the issuer claimed. A verifier may downgrade when deeper evidence does not resolve, and records the lower depth rather than a failure of the higher one. A verifier does not record a depth higher than it executed." + } + }, + "additionalProperties": false + }, + "transparency": { + "type": "string", + "format": "uri", + "description": "SCITT receipt URI. The Trust Record is the Signed Statement; this URI resolves to the inclusion proof (Receipt) on the transparency log." + }, + "cnf": { + "type": "object", + "description": "Confirmation key (RFC 8747) — binds the Trust Record to the TEE-held signing key.", + "required": [ + "jwk" + ], + "properties": { + "jwk": { + "type": "object", + "description": "JWK (RFC 7517) representing the TEE-sealed public key. Keys must carry actual key material: OKP keys require crv and x; EC keys require crv, x, and y.", + "required": [ + "kty" + ], + "properties": { + "kty": { + "type": "string" + }, + "crv": { + "type": "string" + }, + "x": { + "type": "string" + }, + "y": { + "type": "string" + }, + "kid": { + "type": "string" + } + }, + "allOf": [ + { + "if": { + "required": [ + "kty" + ], + "properties": { + "kty": { + "const": "OKP" + } + } + }, + "then": { + "required": [ + "crv", + "x" + ] + } + }, + { + "if": { + "required": [ + "kty" + ], + "properties": { + "kty": { + "const": "EC" + } + } + }, + "then": { + "required": [ + "crv", + "x", + "y" + ] + } + } + ] + } + }, + "additionalProperties": false + }, + "signature": { + "type": "string", + "description": "OPTIONAL embedded signature: base64url (no padding) signature by the cnf key over the canonical JSON form of the record with only this field absent. Every Trust Record MUST be signature-bound per spec section 3.2.2, but enveloped profiles (e.g. JWS, cMCP RuntimeClaim) carry the signature outside the record, so this field is not required by the schema.", + "pattern": "^[A-Za-z0-9_-]+$" + } + }, + "additionalProperties": false, + "allOf": [ + { + "$comment": "A record assembled from another party's evidence has no hardware root. Mirrors the cross-field check in the reference model, so a validator that only reads this schema enforces it too.", + "if": { + "required": [ + "origin" + ], + "properties": { + "origin": { + "required": [ + "kind" + ], + "properties": { + "kind": { + "enum": [ + "third-party-control-plane", + "log-import" + ] + } + } + } + } + }, + "then": { + "properties": { + "runtime": { + "properties": { + "platform": { + "const": "software-only" + } + } + } + } + } + } + ] +} diff --git a/src/trace_tests/modules/tr_pol.py b/src/trace_tests/modules/tr_pol.py index 077150a..7848505 100644 --- a/src/trace_tests/modules/tr_pol.py +++ b/src/trace_tests/modules/tr_pol.py @@ -10,7 +10,7 @@ _DIGEST_RE = re.compile(r"^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$") #: Mirrors `policy.enforcement_mode` in the packaged schema; `test_enum_parity` fails if #: it drifts from that copy. -_VALID_ENFORCEMENT = frozenset({"enforce", "advisory", "silent"}) +_VALID_ENFORCEMENT = frozenset({"enforce", "advisory", "silent", "declared"}) def check(trace: dict[str, Any]) -> list[Finding]: diff --git a/tests/test_level0.py b/tests/test_level0.py index fcb9db9..53521b3 100644 --- a/tests/test_level0.py +++ b/tests/test_level0.py @@ -9,7 +9,7 @@ "intel-tdx", "amd-sev-snp", "azure-cvm-sev-snp", "nvidia-h100", "nvidia-blackwell", "aws-nitro", "arm-cca", "google-confidential-space", "tpm2", "software-only", } -VALID_ENFORCEMENT = {"enforce", "advisory", "silent"} +VALID_ENFORCEMENT = {"enforce", "advisory", "silent", "declared"} VALID_APPRAISAL = {"affirming", "warning", "contraindicated", "none"} From ba2359af379cff8e57f69c6995c6c8be91e06d86 Mon Sep 17 00:00:00 2001 From: lywinged <48041247+lywinged@users.noreply.github.com> Date: Tue, 18 Aug 2026 04:39:41 +0000 Subject: [PATCH 2/2] test(schema): a vector that uses the field whose absence hid the drift The resync in the previous commit fixes the packaged schema. This adds what would have caught it, because the reason nothing caught it is still here otherwise. `schemas/trace-claim.json` sits at `$id` trace-v0.1.json and pins `eat_profile` to the v0.2 sentinel, so it requires a record to declare v0.2 and then rejects two properties v0.2 defines: `signature` and `delegation`. additionalProperties is false, deliberately -- test_unknown_fields_rejected asserts it. Measured against trace-spec's corpus before writing anything: a signed record fails with "'signature' was unexpected" and a signed delegated hop with "'delegation', 'signature' were unexpected". Where that does and does not reach, stated precisely because the first draft of this message overstated it. The file is not shipped -- the wheel packages src/trace_tests only -- and no code under src/ reads it; grep for jsonschema there returns nothing, and TR-ENV checks fields by hand. Its one reader is tests/conftest.py, which feeds tests/test_schema.py, the suite's own Level 0 "structural and schema validity" tests. README.md also links the directory as "Test schemas", so a reader browsing the suite for a reference copy gets the stale one. So the cost is to what this suite can assert, not to any record anyone has scored. A conformance suite whose schema tests run a version behind cannot accept a signed record in them -- which is self-reinforcing, and is why no vector here has a signature. Nothing failed because not one of the six vectors carried a `signature` at all. The schema forbade a field the corpus never used. That is the same shape as the canonicalization defect in tr_sig: the corpus avoided the case the defect lived in. Two in one repository is a pattern, and the answer to both is a vector that uses the field. The two vectors are copied from trace-spec's examples/delegation-link/, generated from a published seed and reproducible by anyone. Nothing compares them to the originals -- a guard that needs another repository checked out is a guard that gets skipped. They are held instead to the property that makes them worth having: they carry the fields whose absence hid the defect, so a vector edited into something unsigned fails loudly rather than quietly. Verified by reverting the schema: the two validation tests and the `$id` check fail, and the self-validation test passes, which is the shape a regression test should have. 180 passed. Signed-off-by: lywinged <48041247+lywinged@users.noreply.github.com> --- tests/conftest.py | 14 +++++ ...st_packaged_schema_accepts_real_records.py | 61 +++++++++++++++++++ tests/vectors/signed_delegated_hop.json | 38 ++++++++++++ tests/vectors/signed_root.json | 34 +++++++++++ 4 files changed, 147 insertions(+) create mode 100644 tests/test_packaged_schema_accepts_real_records.py create mode 100644 tests/vectors/signed_delegated_hop.json create mode 100644 tests/vectors/signed_root.json diff --git a/tests/conftest.py b/tests/conftest.py index c2ed74c..6e3371e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -98,6 +98,20 @@ def invalid_missing_runtime(): return load_vector("invalid_missing_runtime.json") +@pytest.fixture +def signed_root(): + """A signed Trust Record with no delegation block, from trace-spec's + `examples/delegation-link/01-valid-single-hop.json`.""" + return load_vector("signed_root.json") + + +@pytest.fixture +def signed_delegated_hop(): + """The delegated hop from the same chain: signed, and carrying a + `delegation` block.""" + return load_vector("signed_delegated_hop.json") + + @pytest.fixture def invalid_wrong_profile(): return load_vector("invalid_wrong_profile.json") diff --git a/tests/test_packaged_schema_accepts_real_records.py b/tests/test_packaged_schema_accepts_real_records.py new file mode 100644 index 0000000..55c8a9e --- /dev/null +++ b/tests/test_packaged_schema_accepts_real_records.py @@ -0,0 +1,61 @@ +"""The packaged schema against records that carry the fields real records carry. + +The packaged copy sat at `$id` `trace-v0.1.json` while the normative schema moved +to v0.2, and the two diverged on two top-level properties: `signature` and +`delegation`. With `additionalProperties: false` — which +`test_unknown_fields_rejected` shows is deliberate — the packaged copy therefore +rejected **every signed record**, which is every real one, and every delegated +record twice over. + +Nothing here failed, because not one of the six vectors this suite carried had a +`signature` at all. The schema forbade a field the corpus never used. + +That is the same shape as the canonicalization defect in `tr_sig`: the corpus +avoided the case the defect lived in, so the suite stayed green while the thing it +scores was wrong. Two of these in one repository is a pattern rather than an +accident, and the answer to both is a vector that uses the field. + +The two vectors are copied from trace-spec's `examples/delegation-link/`, which is +generated from a published seed and reproducible by anyone. Nothing here compares +them to the originals — a guard that needs another repository checked out is a +guard that gets skipped. They are held instead to the property that makes them +worth having: they carry the fields whose absence hid the defect. A vector edited +into something unsigned stops being a regression test loudly rather than quietly. +""" + +from __future__ import annotations + +import jsonschema +import pytest + + +@pytest.mark.level0 +class TestPackagedSchemaAcceptsRealRecords: + def test_the_vectors_still_carry_the_fields_that_matter( + self, signed_root, signed_delegated_hop + ): + """Half of what makes these regression material. Without it, both records + could be edited down to the shape the old schema accepted and the two + tests below would keep passing against a schema that had drifted back.""" + assert "signature" in signed_root, "signed_root lost its signature" + assert "delegation" not in signed_root, "signed_root is no longer the root case" + assert "signature" in signed_delegated_hop, "the hop lost its signature" + assert "delegation" in signed_delegated_hop, "the hop lost its delegation block" + + def test_a_signed_record_validates(self, schema, signed_root): + """Rejected by the packaged schema before the resync: `'signature' was + unexpected`.""" + jsonschema.validate(signed_root, schema) + + def test_a_signed_delegated_record_validates(self, schema, signed_delegated_hop): + """Rejected twice before the resync: `'delegation', 'signature' were + unexpected`.""" + jsonschema.validate(signed_delegated_hop, schema) + + def test_the_packaged_schema_declares_the_version_it_is(self, schema): + """The drift was visible in one line the whole time. `$id` named v0.1 + while every record the suite scores declares the v0.2 profile.""" + assert schema["$id"].endswith("trace-v0.2.json"), ( + f"packaged schema declares {schema['$id']}, which is not the version " + "the records it validates say they are" + ) diff --git a/tests/vectors/signed_delegated_hop.json b/tests/vectors/signed_delegated_hop.json new file mode 100644 index 0000000..0e3ea0a --- /dev/null +++ b/tests/vectors/signed_delegated_hop.json @@ -0,0 +1,38 @@ +{ + "eat_profile": "tag:agentrust-io.com,2026:trace-v0.2", + "iat": 1785000000, + "subject": "spiffe://acme.example/agent/planner", + "model": { + "provider": "anthropic", + "model_id": "claude-sonnet-4-6" + }, + "runtime": { + "platform": "software-only", + "measurement": "sha256:0000000000000000000000000000000000000000000000000000000000000000" + }, + "policy": { + "bundle_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "enforcement_mode": "enforce" + }, + "data_class": "confidential", + "build_provenance": { + "slsa_level": 0, + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "appraisal": { + "status": "affirming", + "verifier": "https://verifier.example/v1" + }, + "delegation": { + "parent_record_hash": "sha256:b1d65c65d7191e1c6f57f8902548475671edad51035f1542f76a4e45337052b6", + "credential_id": "cred:orchestrator-to-planner" + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "W2BsRtHZW8BVdiCsCxrNxvv4LOfgBhuS_2v7TU7TyHU" + } + }, + "signature": "w1-yxCO_nNRY2pj2rGO065DN25Myk9xLJ13DzL0d9tTTwb3Wq0tnwe2H5U5oEXctynLDJN8xP0CLhLjVcBUmDg" +} diff --git a/tests/vectors/signed_root.json b/tests/vectors/signed_root.json new file mode 100644 index 0000000..d0c7321 --- /dev/null +++ b/tests/vectors/signed_root.json @@ -0,0 +1,34 @@ +{ + "eat_profile": "tag:agentrust-io.com,2026:trace-v0.2", + "iat": 1785000000, + "subject": "spiffe://acme.example/agent/orchestrator", + "model": { + "provider": "anthropic", + "model_id": "claude-sonnet-4-6" + }, + "runtime": { + "platform": "software-only", + "measurement": "sha256:0000000000000000000000000000000000000000000000000000000000000000" + }, + "policy": { + "bundle_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "enforcement_mode": "enforce" + }, + "data_class": "restricted", + "build_provenance": { + "slsa_level": 0, + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "appraisal": { + "status": "affirming", + "verifier": "https://verifier.example/v1" + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "pNTZUXlAITEWbtVbHk6zGRwVD73s0BEakqkKKaFQyZ4" + } + }, + "signature": "m3TAE_Z_chc0gs8HMh1KAN4NECFLP09kiqyQPBDz9w9G0_iYoQ5-I94gpptC1a4xiRloSFLyTdprEmr9CFD_DA" +}