fix: cross-check intent_hash in agent manifest binding verification - #578
Conversation
Signed-off-by: yongikim <yongi.kim08@gmail.com>
imran-siddique
left a comment
There was a problem hiding this comment.
Approving to release the hold gate. Three workflows were sitting in action_required, so cmcp's CI had not run on this at all: two checks reported where this repo normally runs twelve. Releasing them now and I will report before merging.
Verified the defect at main rather than taking the description. expected_identity at verify.py:857 holds eight fields and omits intent_hash, while binding.intent_hash is computed at agent_manifest.py:379 through agent_manifest_sdk.intent_hash(manifest). So the value is derived correctly and then dropped on the floor at the comparison. A claim can assert a fabricated intent_hash and pass binding verification, exactly as you say.
This is the same defect class as cmcp#557 last week, where the chained TPM verifier parsed the signature scheme and digest and then passed only the bare signature. Computed, reported, not enforced. Reported-but-unenforced is worse than absent, because a field appearing in a verification result reads as checked to anyone downstream, and nobody re-derives it.
I also checked the None path, since intent_hash is typed str | None and an issuer may declare no intent. Both sides None compares equal and passes; a claim asserting an intent_hash against a manifest that declares none mismatches and fails. Fail-closed in the direction that matters, so the one-line addition needs no guard around it. Worth stating because a reviewer glancing at a nullable field in an equality check would reasonably ask.
Your security framing is the right level of claim and I want to credit it. "Low current exploitability: the intent field is not yet read by any policy or tool-catalog enforcement path, so this does not bypass an active control" is the sentence most reports would have left out, because it makes the finding sound smaller. It also happens to be the sentence that makes the rest of the report trustworthy. Naming what it does cost, the only mechanical check on an agent's declared purpose with no semantic backstop elsewhere, is the argument that actually carries it.
Filing #577 first and then the PR, rather than opening the PR alone, also means the defect is recorded independently of whether this particular fix is the one that lands.
CI report to follow.
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
CI report, as promised. Releasing the three held workflows took this from two checks to thirteen, all green: tests on 3.11, 3.12 and 3.13 across ubuntu and windows, CodeQL, governance and the Docker build. Merging. Thanks for filing #577 alongside it. |
What
Add
intent_hashto the field set that Step 5 (verify_trace_claim) cross-checks against the Agent Manifest binding, and add a regression test covering the mismatch case.Why
Closes #577.
expected_identityinsrc/cmcp_verify/verify.pycompares 8 fields between the Claim'sgateway.agent_identityand the recomputedAgentManifestBinding, but omitsintent_hash, even thoughverify_agent_manifest_binding()already computes it correctly onbinding.intent_hash. A Claim can currently assert a fabricatedintent_hashand still passagent_manifest.bindingverification.Security impact
Low current exploitability: the
intentfield is not yet read by any policy or tool-catalog enforcement path, so this does not bypass an active control. But it removes the only mechanical check on an agent's declared purpose (Agent Manifest spec §3.9), and the spec itself (§3.9.1) notes there is no semantic backstop for this anywhere else in the ecosystem. Does not touchsrc/cmcp_gateway/audit/,src/cmcp_gateway/tee/, orsrc/cmcp_gateway/policy/.Test plan
pytest tests/unit/ -vpasses (addstest_agent_manifest_binding_intent_hash_mismatch_failstotests/unit/test_verify.py, mirroring the existingtest_agent_manifest_binding_mismatch_fails)ruff check src/cmcp_verify/verify.py tests/unit/test_verify.pypassesmypy src/cmcp_verify/verify.pypassesbandit -r src/ -c pyproject.tomlpassesDCO sign-off
Developer Certificate of Origin (https://developercertificate.org).