Summary
The A2A HTTP route validates that the supplied API key exists globally, then loads the target agent_id independently. In the replay, an API key belonging to agent A is rejected on agent B's normal chat route, but the same key is accepted on agent B's A2A route. The accepted caller can set/get push configuration, cancel a task by taskId, and reuse a context key to receive agent B history.
Affected Target Verified
- Repository:
https://github.com/evolution-foundation/evo-ai
- Component: FastAPI A2A routes in
src/api/a2a_routes.py
- Affected commit verified:
7ff7a1455c68c9da6aab7f1d16617570374f81b8
- Vulnerability type: Authorization bypass / cross-agent task and context authority
Preconditions
- The deployment exposes
/api/v1/a2a/{agent_id}.
- Agents use per-agent API keys as a security boundary.
- A caller knows or guesses a target task id or context/message id, or obtains it through normal task flow.
Steps To Reproduce
From the attachment directory:
The script clones the target repository, checks out the verified commit, prepares a local test environment, and runs the bundled PoC. It does not contact attacker-controlled services; the setup only downloads the public repository and normal build dependencies.
To run against an existing checkout:
EVO_AI_TARGET_DIR=/path/to/checkout bash reproduce.sh
The wrapper accepts EVO_AI_TARGET_DIR for an existing checkout. It also accepts the target commit override documented in attachments/reproduce.sh.
Technical Details
a2a_routes.py calls the global API-key verification path before loading the target route agent. The normal chat route separately checks that the API key matches the specific target agent config. The A2A route then stores push config by bare taskId and promotes inbound messageId into downstream conversation context.
Expected Behavior
An API key accepted on /api/v1/a2a/{agent_id} should be authorized for that
specific target agent before any A2A method is dispatched. Task, push-config,
and context-continuation state should remain scoped to the authenticated agent
or principal.
Actual Behavior
- Invalid A2A key is rejected with 401.
- Agent A key is rejected on agent B normal chat route.
- Agent A key is accepted on agent B A2A route for push config set/get and cancel.
- Same-context
message/send returns Alice history; fresh-context negative control does not.
Impact
A caller with one valid agent key can operate on another agent's A2A control
plane when the A2A route only validates that the key exists globally. The
negative control is important: the same agent A key is rejected on agent B's
normal chat route, so the A2A route is weaker than the product's existing
per-agent API-key boundary.
If agents represent different users, workspaces, integrations, or data
contexts, this lets a caller cross from their own agent into another agent's
A2A task surface. The reproduced operations include setting and reading push
configuration, canceling task state, and reusing a context/message key to
receive prior history from the target agent.
Push configuration control is especially sensitive because it can redirect
future task updates or artifacts to a callback chosen by the wrong key holder.
Context history reuse can expose prior prompts and responses from the target
agent. Cancellation lets the wrong key holder interfere with another agent's
task lifecycle.
Suggested Fix
Bind the presented API key to the target agent_id before dispatching A2A JSON-RPC methods. Store task and push-config state under the authenticated agent/principal as well as task id. Treat context/message ids as correlation handles, not authorization handles.
Attachments
attachments.zip
Summary
The A2A HTTP route validates that the supplied API key exists globally, then loads the target
agent_idindependently. In the replay, an API key belonging to agent A is rejected on agent B's normal chat route, but the same key is accepted on agent B's A2A route. The accepted caller can set/get push configuration, cancel a task bytaskId, and reuse a context key to receive agent B history.Affected Target Verified
https://github.com/evolution-foundation/evo-aisrc/api/a2a_routes.py7ff7a1455c68c9da6aab7f1d16617570374f81b8Preconditions
/api/v1/a2a/{agent_id}.Steps To Reproduce
From the attachment directory:
The script clones the target repository, checks out the verified commit, prepares a local test environment, and runs the bundled PoC. It does not contact attacker-controlled services; the setup only downloads the public repository and normal build dependencies.
To run against an existing checkout:
The wrapper accepts
EVO_AI_TARGET_DIRfor an existing checkout. It also accepts the target commit override documented inattachments/reproduce.sh.Technical Details
a2a_routes.pycalls the global API-key verification path before loading the target route agent. The normal chat route separately checks that the API key matches the specific target agent config. The A2A route then stores push config by baretaskIdand promotes inboundmessageIdinto downstream conversation context.Expected Behavior
An API key accepted on
/api/v1/a2a/{agent_id}should be authorized for thatspecific target agent before any A2A method is dispatched. Task, push-config,
and context-continuation state should remain scoped to the authenticated agent
or principal.
Actual Behavior
message/sendreturns Alice history; fresh-context negative control does not.Impact
A caller with one valid agent key can operate on another agent's A2A control
plane when the A2A route only validates that the key exists globally. The
negative control is important: the same agent A key is rejected on agent B's
normal chat route, so the A2A route is weaker than the product's existing
per-agent API-key boundary.
If agents represent different users, workspaces, integrations, or data
contexts, this lets a caller cross from their own agent into another agent's
A2A task surface. The reproduced operations include setting and reading push
configuration, canceling task state, and reusing a context/message key to
receive prior history from the target agent.
Push configuration control is especially sensitive because it can redirect
future task updates or artifacts to a callback chosen by the wrong key holder.
Context history reuse can expose prior prompts and responses from the target
agent. Cancellation lets the wrong key holder interfere with another agent's
task lifecycle.
Suggested Fix
Bind the presented API key to the target
agent_idbefore dispatching A2A JSON-RPC methods. Store task and push-config state under the authenticated agent/principal as well as task id. Treat context/message ids as correlation handles, not authorization handles.Attachments
attachments.zip