[Bug]: Agent card signature verification raises raw binascii.Error on a malformed protected header instead of SignatureVerificationError
Mantenedores costumam responder em até 2 dias
Avaliação
Esta issue ainda não foi avaliada.
Descrição
What happened?
create_signature_verifier's verification loop (src/a2a/utils/signing.py:139-159) only catches PyJWTError:
for agent_card_signature in agent_card.signatures:
try:
protected_header_json = base64url_decode(
agent_card_signature.protected.encode('utf-8')
).decode('utf-8') # may raise binascii.Error / UnicodeDecodeError
protected_header = json.loads(protected_header_json) # may raise JSONDecodeError
...
verification_key = key_provider(kid, jku) # may raise anything (e.g. network errors)
jwt.decode(...)
break
except PyJWTError:
continue
agent_card_signature.protected is an attacker-controlled field. A malformed value makes base64url_decode raise a raw binascii.Error, which escapes the verifier entirely — violating the module's own contract: the canonicalization step just above explicitly converts failures to InvalidSignaturesError "so every failure on this path is a SignatureVerificationError", but the per-signature loop does not.
It also breaks the documented multi-signature semantic ("succeeds if at least one signature is valid"): a card holding [malformed_signature, valid_signature] raises instead of verifying.
Additionally, any exception raised by key_provider (e.g. a network failure while fetching the JKU) aborts the whole loop for the same reason.
Reproduction
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'],
)
card = AgentCard(
name='t',
version='1.0',
signatures=[
AgentCardSignature(protected='!!!not-base64url!!!', signature='aaa')
],
)
try:
verifier(card)
except SignatureVerificationError as e:
print(f'contract respected: {type(e).__name__}: {e}')
except Exception as e:
print(f'CONTRACT BROKEN -> {type(e).__name__}: {e}')
Observed output:
CONTRACT BROKEN -> Error: Invalid base64-encoded string: number of data characters (13) cannot be 1 more than a multiple of 4
Expected behavior
Every failure on the verification path should surface as SignatureVerificationError (per the contract established at signing.py:124-128), and a malformed signature should count as "invalid" so remaining signatures are still attempted.
Suggested fix
Widen the per-signature except clause, e.g.:
except (PyJWTError, ValueError, binascii.Error, UnicodeDecodeError):
continue
or wrap the loop body so parse/key-provider failures are logged and treated as an invalid signature. This is non-breaking for correct inputs and only converts escapes into the documented error type.
Happy to submit a PR with tests.
Code of Conduct
- I agree to follow this project's Code of Conduct
- Linguagem predominante
- Python
- Estrelas
- 2.2k
- Forks
- 518
- Merge médio
- 3d 12h
- PRs com merge (30d)
- 39
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Tem um modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de a2aproject/a2a-python
-
[Bug]: `A2A-Version` validation ignores minor versions and the query parameterTalvez já em andamento @rohityan assumiu há 60 dias. Abertacomponent: server status:awaiting response
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 75/100
a2aproject/a2a-python#1204 · 3 comentários · 1 responsável ·
Mantenedores costumam responder em até 2 dias
-
[Bug]: REST task/request id sanitizationTalvez já em andamento @Linux2010 assumiu há 119 dias. Abertamaintainers-only
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 65/100
a2aproject/a2a-python#805 · 1 comentário ·
Mantenedores costumam responder em até 2 dias
-
[Bug]: TaskEventStream methods don't receive ServerCallContextTalvez já em andamento @rohityan assumiu há 2 dias. Aberta
a2aproject/a2a-python#1374 · 2 responsáveis ·
Mantenedores costumam responder em até 2 dias
-
[Bug]: v0.3 JSON-RPC compat adapter returns -32603 for all A2A errors (e.g. TaskNotFound should be -32001)Talvez já em andamento @shwetangsinha0509 assumiu há 2 dias. Abertacomponent: server status:awaiting response
a2aproject/a2a-python#1368 · 2 comentários · 1 responsável ·
Mantenedores costumam responder em até 2 dias
-
[Bug]: VersionedDatabaseTaskStore get() and delete() fail with "no such table" when initialize() was not calledTalvez já em andamento @cchinchilla-dev assumiu há 3 dias. Abertacomponent: server status:awaiting response
a2aproject/a2a-python#1356 · 1 comentário · 1 responsável ·
Mantenedores costumam responder em até 2 dias
Todas as issues de a2aproject/a2a-python
Issues semelhantes
-
bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
conda-forge/conda-forge-bot#7025 ·
Mantenedores costumam responder em até 1 dia
-
area:ci
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 74/100
catincloud-labs/dagster-dex#149 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 84/100
home-assistant/core#188424 ·
Mantenedores costumam responder em até 1 dia
-
Review backlog (docs-currency): README 'CI run' link lands on a run whose overall badge is cancelledAbertaagent/reviewer needs-kind needs-triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 63/100
projectbluefin/utah#880 · 2 comentários ·
Mantenedores costumam responder em até 1 dia
-
LSan reports obj_gen leak on post-parse usage() exits (aarch64)Talvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
redis/memtier_benchmark#850 ·
Mantenedores costumam responder em até 1 dia