Skip to content

fix(transport): clear stale optional cA2A metadata on reused SDK messages - #167

Merged
carloshvp merged 1 commit into
agentrust-io:mainfrom
miyannishar:sdk-stale-metadata-fix
Sep 13, 2026
Merged

fix(transport): clear stale optional cA2A metadata on reused SDK messages#167
carloshvp merged 1 commit into
agentrust-io:mainfrom
miyannishar:sdk-stale-metadata-fix

Conversation

@miyannishar

Copy link
Copy Markdown
Contributor

What

attach_to_sdk_message now attaches cA2A metadata against the SDK message's current metadata instead of a throwaway {}, so an absent optional key (sealed_payload, caller_offer, holder_proof) is cleared when a Message is reused across calls. Adds round-trip tests covering all three optional keys.

Why

The bridge mutates an SDK Message in place. It attached via a2a_adapter.attach_ca2a_metadata({}, request), but the dict adapter clears an absent optional key with meta.pop(KEY_..., None) — run against {} those pops are no-ops, so only present keys were overlaid onto the message's stale metadata and an earlier request's optional key survived. The dict adapter itself is correct; only the SDK bridge diverged. Attaching against the real current metadata restores parity with the dict adapter.

Closes #166.

Security impact

Fixes a cross-request payload-confusion bug on the sealed-channel carrier: a reused SDK Message could ship an earlier task's sealed_payload (and likewise caller_offer / holder_proof). With holder-proof enforcement it fails closed (HolderProofInvalid); in advisory/unenforced modes the callee would open the stale ciphertext as this task's payload. The fix ensures reused messages reflect only the current request.

Test plan

  • pytest passes (tests/unit/ + tests/conformance/: 604 passed, 2 skipped; 3 new tests red before the fix, green after)
  • ruff check passes
  • mypy passes (ruff format --check and bandit also clean)
  • Manual test performed

Repro before the fix (against the real a2a-sdk):

from a2a.types import Message
from ca2a_runtime.transport import a2a_sdk
from tests.unit.test_a2a_sdk_bridge import _request
msg = Message(message_id="m1")
a2a_sdk.attach_to_sdk_message(msg, _request(sealed_payload=b"SECRET"))
a2a_sdk.attach_to_sdk_message(msg, _request())
print(a2a_sdk.parse_sdk_message(msg).sealed_payload)  # was b"SECRET", now None

DCO sign-off

…ages

attach_to_sdk_message mutates an SDK Message in place but did not strip
stale optional cA2A keys (sealed_payload, caller_offer, holder_proof) when
the message was reused across calls: it attached against a throwaway {}, so
the dict adapter's meta.pop(...) clears were no-ops and only present keys
were overlaid onto stale metadata. A later task could then ship an earlier
task's sealed ciphertext (cross-request payload confusion).

Attach against the message's current metadata instead, so the adapter's pops
apply -- a single source of truth with the dict adapter. Adds round-trip
tests covering all three optional keys.

Closes agentrust-io#166

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Nishar <nishar@dayos.com>
@miyannishar
miyannishar requested review from a team, carloshvp and zohebk8s as code owners September 13, 2026 16:54

@carloshvp carloshvp 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.

Reviewed commit 1e038a9. No blocking findings.

The bridge now applies the dict adapter to the current metadata, so absent sealed_payload, caller_offer, and holder_proof fields are removed on reuse while unrelated metadata is preserved. This fixes the stale cross-request carrier data without changing verification policy.

Validation in an isolated checkout on Python 3.13 with the real a2a-sdk 1.1.2: unit and conformance suites passed (604 passed, 2 skipped); ruff check, ruff format --check, mypy, and Bandit passed. All three new regression tests fail against the parent implementation. An additional eight-combination optional-field matrix, repeated three times per combination, verified clearing, replacement, unrelated metadata preservation, routing preservation, and extension idempotence.

Approval covers this exact head. The hosted CI matrix is not present in the current check rollup; local validation does not replace remaining repository gates.

@codecov-commenter

Copy link
Copy Markdown

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

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@carloshvp
carloshvp enabled auto-merge September 13, 2026 19:57
@carloshvp
carloshvp merged commit a711e61 into agentrust-io:main Sep 13, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(transport): attach_to_sdk_message leaks stale optional cA2A metadata across reused messages

3 participants