a2ui-project / a2ui-project/a2ui

[BUG]: Validation Bypass via Mixed Client/Server Messages in A2uiValidator

Open
#2,579 1 comment 0 reactions 1 assignee Claimed by @Varun-S10 View on GitHub
P2 status: first-line-handled status: waiting-for-author-response type: bug
Dominant language
TypeScript
Stars
16.4k
Forks
1.3k
Avg merge
2d 13h
Merged PRs (30d)
134

Description

# Location
agent_sdks/python/a2ui_core/src/a2ui/core/validating/validator.py:248

# Description
The A2uiValidator.validate method contains a logic flaw in its is_client_payload detection. It iterates over a list of messages and checks if any() message contains client-specific keys (action, error, or data). If this evaluates to True, the method immediately returns, skipping all protocol envelope, catalog schema, and topology integrity validation. An attacker controlling the A2UI JSON stream (e.g., a prompt-injected agent) can craft a payload containing both a dummy client message and malicious server messages (e.g., un-whitelisted components, recursive structures, or exploit payloads). The presence of the client message satisfies the any() condition, completely bypassing the SDK's validation checks and passing the unvalidated malicious payload to downstream processing.

# Impact
Bypass of the primary validation layer (TB-1). Malicious agents can deliver malformed or exploit-bearing A2UI JSON payloads that would otherwise be rejected, enabling downstream attacks such as Prototype Pollution or Resource Exhaustion depending on the renderer's sink behaviors.

# Mitigation
Modify the is_client_payload check to ensure all messages belong to the client namespace, e.g., using all(...) instead of any(...). Alternatively, enforce that payloads cannot mix client and server messages, raising an error if a mixed payload is detected.

# Reproduction Steps
Create an A2UI payload with multiple messages.
In the first message, include a client key: {\"action\": {\"functionCall\": {\"name\": \"dummy\"}}}.
In the second message, include an invalid or malicious server message, e.g., a createSurface message with invalid component schemas or recursive definitions.
Call A2uiValidator.validate() on this payload array.
Observe that the validator silently accepts the payload without raising an A2uiValidatorError.

# Evidence
```
is_client_payload = any(
isinstance(m, dict) and any(k in m for k in (\"action\", \"error\", \"data\"))
for m in messages
)
if is_client_payload:
return
```

# Reasoning
The A2uiValidator.validate method takes an a2ui_payload and processes it into a list of messages. It then contains a check: is_client_payload = any(isinstance(m, dict) and any(k in m for k in ("action", "error", "data")) for m in messages). If is_client_payload evaluates to True, the function immediately returns without validating any of the messages. An attacker can construct a payload consisting of a list of messages where at least one message contains an "action", "error", or "data" key, and the subsequent messages can be arbitrary unvalidated payloads. Because of the early return, the rest of the payload completely bypasses schema, topological, and protocol validation. This clearly allows maliciously crafted server messages to bypass validation if mixed with a dummy client message.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.