Current behavior
verify_audit_bundle() is a separately exported verifier entry point and returns AuditBundleResult, but at current main (2c0a601805bfb31fddc49db32ae122bae8e2251e) it assumes the externally supplied bundle and every member of entries already have object shape.
The first verification loop does:
entries = bundle_json.get("entries", [])
...
for i, entry in enumerate(entries):
body = {k: v for k, v in entry.items() if k != "entry_hash"}
So malformed but JSON-valid inputs such as:
{"entries": ["unexpected"]}
or
reach .items() and escape as AttributeError instead of producing an AuditBundleResult(verified=False, ...).
A truthy non-list entries value also enters the loop. For example, a string is iterated character by character before failing on the first character's missing .items().
The optional claim-binding half has the same structural assumption in nested reads such as:
chain = claim_json.get("gateway", {}).get("audit_chain", {})
transcript = claim_json.get("trace", {}).get("tool_transcript", {})
...
x_b64 = claim_json.get("trace", {}).get("cnf", {}).get("jwk", {}).get("x", "")
which can raise if an intermediate value is a scalar/list rather than an object.
Why this is distinct from #592
#592 concerns verify_trace_claim() continuing into structure-dependent claim verification after its Pydantic schema check has already failed.
This issue is the independent public verify_audit_bundle() API. It has no schema gate at all for the bundle and returns a different result type. Fixing #592 does not change this path.
Expected invariant
Malformed external audit-bundle structure should fail closed as verifier evidence, not terminate the caller with a Python shape exception.
At minimum, verify_audit_bundle() should either:
- establish the bundle/entry object and array boundaries before content verification; or
- defensively classify malformed structure into
AuditBundleResult.failures.
The verifier should not need to infer cryptographic meaning from a value whose JSON container type has not first been established.
Focused regression matrix
- top-level
bundle_json object with missing/empty entries -> existing bundle has no entries behavior preserved;
entries = string/object/integer/bool -> failed AuditBundleResult, never an exception;
entries = list containing string/integer/list/null -> failed AuditBundleResult, never an exception;
- one valid entry control -> existing hash/link verification unchanged;
- when
claim_json is supplied, malformed gateway, gateway.audit_chain, trace, trace.tool_transcript, and trace.cnf intermediates -> failed AuditBundleResult, never an exception.
No wire-format or normative specification change appears necessary. This is verifier exception-safety and structural failure classification at the audit-bundle boundary.
Current behavior
verify_audit_bundle()is a separately exported verifier entry point and returnsAuditBundleResult, but at currentmain(2c0a601805bfb31fddc49db32ae122bae8e2251e) it assumes the externally supplied bundle and every member ofentriesalready have object shape.The first verification loop does:
So malformed but JSON-valid inputs such as:
{"entries": ["unexpected"]}or
{"entries": [1]}reach
.items()and escape asAttributeErrorinstead of producing anAuditBundleResult(verified=False, ...).A truthy non-list
entriesvalue also enters the loop. For example, a string is iterated character by character before failing on the first character's missing.items().The optional claim-binding half has the same structural assumption in nested reads such as:
which can raise if an intermediate value is a scalar/list rather than an object.
Why this is distinct from #592
#592 concerns
verify_trace_claim()continuing into structure-dependent claim verification after its Pydantic schema check has already failed.This issue is the independent public
verify_audit_bundle()API. It has no schema gate at all for the bundle and returns a different result type. Fixing #592 does not change this path.Expected invariant
Malformed external audit-bundle structure should fail closed as verifier evidence, not terminate the caller with a Python shape exception.
At minimum,
verify_audit_bundle()should either:AuditBundleResult.failures.The verifier should not need to infer cryptographic meaning from a value whose JSON container type has not first been established.
Focused regression matrix
bundle_jsonobject with missing/emptyentries-> existingbundle has no entriesbehavior preserved;entries= string/object/integer/bool -> failedAuditBundleResult, never an exception;entries= list containing string/integer/list/null -> failedAuditBundleResult, never an exception;claim_jsonis supplied, malformedgateway,gateway.audit_chain,trace,trace.tool_transcript, andtrace.cnfintermediates -> failedAuditBundleResult, never an exception.No wire-format or normative specification change appears necessary. This is verifier exception-safety and structural failure classification at the audit-bundle boundary.