Skip to content

fix(verify): classify malformed audit-bundle shapes - #597

Draft
altrudev wants to merge 3 commits into
agentrust-io:mainfrom
altrudev:fix/audit-bundle-shape-boundary-593
Draft

fix(verify): classify malformed audit-bundle shapes#597
altrudev wants to merge 3 commits into
agentrust-io:mainfrom
altrudev:fix/audit-bundle-shape-boundary-593

Conversation

@altrudev

@altrudev altrudev commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

What

Closes #593 after review and merge. The public verify_audit_bundle() boundary now classifies malformed external bundle and optional claim containers as a failed AuditBundleResult, rather than leaking incidental Python TypeError or AttributeError exceptions.

The implementation adds narrow structural guards before interpretation. It does not change wire format, hash construction, evidence semantics, or the existing chain, receipt, signature, and claim-binding checks.

Scope

  • Require an object bundle root, an array of entries, and an object for each entry.
  • Require an object claim root when supplied, and object shapes for gateway, gateway.audit_chain, gateway.call_summary, trace, trace.tool_transcript, trace.cnf, and trace.cnf.jwk when present.
  • Return a path-specific structural failure with the applicable entry count.
  • Preserve the existing missing-entries result, valid-entry success, and downstream verification logic.

The focused test file covers 20 cases: nine malformed-entry vectors, seven malformed claim-container vectors, two malformed roots, and two controls. The claim-side fixture computes the actual entry hash so structural failures are not masked by an unrelated hash or chain failure.

Test-first evidence

The original reproducer at e7a41a4dd7e16a265d4871383881e680232040eb was independently run through an isolated governed test executor: 1 passed, 15 expected failures, reproducing the documented exception escapes. The production implementation and ordinary regression assertions are published at bec8751afea9e8c303429f5cfe63d46c55a05a92.

Validation

The existing hosted CI run 34197122450 completed successfully on the PR head. All six Python 3.11, 3.12, and 3.13 Linux/Windows matrix jobs passed, as did governance. The Python 3.12.14 Ubuntu log records:

  • 1,649 passed, 6 skipped, 1 warning; all 20 focused malformed-shape cases passed.
  • Coverage 87.63%, above the 70% threshold.
  • Ruff passed; mypy passed on 54 source files.
  • Bandit identified no issues; pip-audit found no known vulnerabilities in audited dependencies, excluding the editable local distribution.

Fuzzing and CodeQL are green in the current head's check results. The remaining failed overall check is the maintainer-approval gate, not a verifier-test failure. No manual workflow rerun was requested. Hardware-dependent skips are not represented as hardware validation.

Review status

The implementation is published but this PR remains draft and unmerged pending maintainer review and release-gate reconciliation. The earlier recovery harness is not part of the proposed production diff. No deployment, accreditation, or comprehensive security-assurance claim is made.

DCO: implementation commits are signed off. AI-assistance disclosure: ChatGPT assisted with source triage, adversarial matrix design, implementation drafting, and validation reconciliation. altrudev retains responsibility for the contribution.

…st-io#593)

Signed-off-by: altrudev <266135212+altrudev@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

🟡 Contributor Check: MEDIUM

Check Result
Profile MEDIUM
Credential LOW
Overall MEDIUM

Automated check by AgenTrust Contributor Check.

@github-actions github-actions Bot added the needs-review:MEDIUM Contributor check flagged MEDIUM risk label Aug 30, 2026
@imran-siddique

imran-siddique commented Aug 31, 2026

Copy link
Copy Markdown
Member

Batch response for this cluster is here: agentrust-io/agent-manifest#357 (comment)

Short version: the finding class is real and welcome. Your CI had never run, held under first-time-contributor gating, until I released 36 runs across your PRs an hour ago, and five of your eight are now red. Please fix those, sequence trace-spec#258 against #252 which touch the same two files, and tell me the order you want them reviewed in.

Copy link
Copy Markdown
Contributor Author

I reconciled the released CI failure here as well. Lint/type/security were already green; the red pytest step was the draft reproducer doing exactly what #593 reports: malformed bundle/claim containers escaped as TypeError / AttributeError.

At e7a41a4 the tests now xfail only when those known host-language exceptions escape. If verify_audit_bundle() returns an AuditBundleResult, the original verified == False / failures assertions run normally. Missing-entries remains an ordinary passing control.

That keeps this draft executable without treating a known-current defect as a general CI regression, and it does not pretend #593 is implemented. I am keeping #597 out of the review queue until the narrow structural implementation is ready. Fresh CI is currently waiting on workflow approval; no green claim yet.

@codecov-commenter

codecov-commenter commented Aug 31, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 58.33333% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/cmcp_verify/verify.py 58.33% 5 Missing ⚠️

📢 Thoughts on this report? Let us know!

@imran-siddique imran-siddique left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review/test follow-up on e7a41a4dd7e1.

This draft is a reproduction suite, with all 15 malformed-bundle/claim cases still xfailed on the current implementation. It therefore records the escapes but does not fix them. Fresh CI is red with 20 failed, 1439 passed, 6 skipped, 15 xfailed, including the shared SDK MISMATCH and TPM error-message failures. The local distribution-smoke failure is an existing review-environment metadata mismatch (0.4.0 runtime versus installed 0.4.1), not evidence of a new defect in these test-only changes. Keep this draft pending the #593 fix; turn the reproductions into required passing regressions when the implementation lands. The SDK compatibility follow-up is #616.

Full local python -m pytest -q (Windows, Python 3.12, existing review environment): 1 failed, 1498 passed, 16 skipped, 15 xfailed, 1 warning in 73.11s (0:01:13).

Fresh workflow evidence: 33346156403 (failure).

Classify malformed bundle entries and claim intermediary containers before hashing, receipt checks, or claim binding interpretation. Preserve the existing missing-entry diagnostic and valid bundle behavior.

Closes agentrust-io#593.

AI-assisted; Altru.dev remains responsible for the change and verification.

Signed-off-by: Altru.dev <altrudevelop@gmail.com>
@altrudev altrudev changed the title test(verify): reproduce malformed audit-bundle shape escapes fix(verify): classify malformed audit-bundle shapes Sep 9, 2026
@qubeena07

Copy link
Copy Markdown
Collaborator

Reviewed the production fix at bec8751, not the earlier reproducer commits. Found two more escape routes the shape guard at the top of verify_audit_bundle does not cover, same bug class this PR and #593 exist to close. Reproduced both directly against this branch, not just read the code.

First, around line 737:

"tools_invoked": sorted(
    {
        entry["tool_name"]
        for entry in tool_calls
        if entry.get("tool_name") is not None
    }
),

If tool_name is a list instead of a string, this raises TypeError: unhashable type: list. Confirmed:

bundle = {"entries": [{
    "entry_type": "tool_call",
    "tool_name": ["x"],
    "call_id": "c1",
    "entry_hash": "deadbeef",
    "prev_entry_hash": "genesis",
}]}
claim = {
    "gateway": {"audit_chain": {"tip": "sha256:deadbeef"}, "call_summary": {}},
    "trace": {"tool_transcript": {"hash": "sha256:deadbeef", "call_count": 1}},
}
verify_audit_bundle(bundle, claim)
# TypeError: unhashable type: 'list'

Second, around line 658:

evidence_type = ev.get("evidence_type", "")
if evidence_type not in _EXTERNAL_EVIDENCE_TYPES:

Same problem, if evidence_type is a list the in check against a set raises TypeError: unhashable type: list instead of recording a failure. Confirmed the same way with an external_execution_evidence block whose evidence_type is a list.

Both reachable without a valid hash chain, since the chain check earlier in the function appends to failures and keeps going rather than returning early, so a caller does not need to get past that first to reach these two.

The top of function guard only checks that each bundle entry is a dict, not that individual fields inside a dict have the right type, so these two slip through the same way the original TypeError/AttributeError cases in #593 did before this PR's shape checks were added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review:MEDIUM Contributor check flagged MEDIUM risk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

verify_audit_bundle can raise on malformed bundle entry shapes instead of returning AuditBundleResult

4 participants