Security Considerations for A2UI: A Defense-in-Depth Approach to Agent-Generated UI
A2UI treats every UI description as declarative data rather than executable code, enforcing strict component catalog validation, sandboxed function calls, and data-model isolation to prevent arbitrary script injection and state-scraping attacks.
The google/A2UI repository implements a security-first architecture for agent-to-user-interface communication. When evaluating the security considerations for A2UI, developers must understand how the framework mitigates risks from untrusted agents while maintaining flexibility. The system employs multiple defense layers—from non-executable JSON payloads to orchestrator-level data filtering—to ensure that malicious agents cannot compromise client applications.
Declarative, Non-Executable UI Architecture
A2UI's fundamental security principle is that UI descriptions are data, not code. Agents emit JSON structures that describe what should appear on screen, eliminating the risk of arbitrary script injection.
According to the README.md, the framework adopts a "security-first" philosophy where the UI payload strictly defines presentation elements without containing executable logic. This declarative approach ensures that even if an agent is compromised, it cannot deliver malicious JavaScript or other harmful code to the client.
Component Catalog Enforcement
The renderer validates every UI element against a trusted component catalog before instantiation. Clients register only pre-audited components (such as Button or Card), and the system rejects any unknown component types.
As documented in README.md, this restriction prevents agents from requesting the rendering of malicious or unexpected widgets. The following TypeScript implementation from the Lit renderer demonstrates catalog validation:
// src/catalog.ts
import { ComponentDef } from '@a2ui/types';
// The client's trusted component catalog
export const CATALOG: Record<string, ComponentDef> = {
Button: { /* …definition… */ },
Card: { /* …definition… */ },
// Add only components you have audited
};
/**
* Safely resolves a component name from the A2UI payload.
* Throws if the component is not in the trusted catalog.
*/
export function getComponentDef(name: string): ComponentDef {
const def = CATALOG[name];
if (!def) {
// Reject unknown components – prevents arbitrary code execution
throw new Error(`Component "${name}" is not registered in the catalog`);
}
return def;
}
The renderer calls getComponentDef for each received component, guaranteeing that only vetted UI elements are instantiated.
Sandboxed Execution via functionCall
Agents trigger client-side behavior exclusively through a predefined functionCall mechanism. Each call is validated against a whitelist of safe functions, preventing raw code execution on the client.
The client_to_server_actions.md documentation specifies that this sandboxed execution model ensures agents cannot invoke arbitrary JavaScript or access unauthorized browser APIs. Only explicitly registered functions—such as data submission handlers—can be executed, with strict parameter validation at the boundary.
Data-Model Isolation and Orchestrator Routing
When clients transmit their full data model using sendDataModel: true, A2UI implements strict isolation to prevent state-scraping attacks. The data is visible only to the originating agent or a trusted orchestrator, which must strip unrelated surface state before forwarding payloads to sub-agents.
As detailed in client_to_server_actions.md and the state-scraping risk section, this prevents malicious sub-agents from accessing sensitive information belonging to other agents. The following Python example illustrates the orchestrator's stripping logic:
# orchestrator.py – simplified interceptor
async def strip_data_model(request_payload, target_agent, session):
"""Remove surfaces that do not belong to the target sub‑agent."""
dm = request_payload["metadata"]["a2uiClientDataModel"]
if not dm:
return request_payload
filtered = {
sid: state
for sid, state in dm["surfaces"].items()
if session.state.get(f"owner_of_{sid}") == target_agent.name
}
request_payload["metadata"]["a2uiClientDataModel"]["surfaces"] = filtered
return request_payload
This ensures a sub-agent only sees the slice of the data model it owns, mitigating unauthorized data access.
Authentication and Authorization Guardrails
A2UI sample applications demonstrate a two-step security model separating identity verification from access control. The Personalized Learning demo illustrates this pattern using Firebase authentication combined with server-side authorization checks.
In [samples/personalized_learning/src/firebase-auth.ts](https://github.com/google/A2UI/blob/main/samples/personalized_learning/src/firebase-auth.ts#L112-L127), the implementation first validates the user identity through Firebase, then confirms authorization via a server-side endpoint:
// src/firebase-auth.ts (excerpt)
export async function isUserAuthorized(): Promise<boolean> {
const user = await getCurrentUser(); // Firebase auth only
if (!user) return false;
// Server‑side endpoint verifies the e‑mail against an allowlist
const resp = await fetch('/api/check-access', {
headers: { Authorization: `Bearer ${await user.getIdToken()}` },
});
const data = await resp.json();
return data.authorized === true;
}
The client never trusts the UI payload alone; it confirms server-side permission before proceeding, keeping identity and access control separate from UI generation.
Developer-Level Security Responsibilities
While A2UI provides the architectural foundation, the final security posture depends on host application implementation. Sample READMEs explicitly warn developers to apply input sanitization, Content-Security-Policy (CSP) headers, and strict sandboxing for embedded content.
The Lit client README and shell README emphasize that developers must validate all inputs and configure CSP headers to prevent injection attacks. Additionally, the contact_multiple_surfaces sample illustrates handling of untrusted agent data and prompt-injection risks, reminding developers that agent-generated content requires the same scrutiny as user-generated input.
Summary
- Declarative UI: A2UI uses JSON data descriptions rather than executable code, eliminating script injection vectors.
- Catalog Enforcement: Only pre-registered components in the trusted catalog are rendered, blocking unknown widget types.
- Sandboxed Execution: The
functionCallmechanism restricts agents to whitelisted functions, preventing arbitrary code execution. - Data Isolation: Orchestrators must strip unrelated surface state from data models to prevent state-scraping between agents.
- Auth Separation: Authentication (Firebase) and authorization (server-side checks) remain distinct from UI generation logic.
- Developer Duties: Host applications must implement CSP headers, input sanitization, and proper sandboxing to maintain security boundaries.
Frequently Asked Questions
How does A2UI prevent arbitrary code execution from malicious agents?
A2UI prevents code execution by treating all UI descriptions as declarative JSON data rather than executable scripts. The framework enforces a strict component catalog where only pre-vetted components can be instantiated, and all agent-triggered actions are funneled through a sandboxed functionCall mechanism that validates calls against a whitelist of safe functions.
What is the "state-scraping" risk in A2UI and how is it mitigated?
State-scraping occurs when a malicious sub-agent attempts to access the full client data model containing other agents' sensitive surface states. A2UI mitigates this by requiring orchestrators to strip unrelated surface data before forwarding payloads to sub-agents. When sendDataModel: true is enabled, the orchestrator must filter the a2uiClientDataModel to include only surfaces owned by the target agent, preventing unauthorized data access.
Why must developers implement a component catalog in A2UI?
The component catalog acts as a whitelist of trusted UI elements that the renderer is permitted to instantiate. By requiring developers to explicitly register components (such as Button or Card) in the catalog, A2UI ensures that agents cannot request the rendering of malicious or unexpected widgets. The getComponentDef function rejects any component type not present in the catalog, blocking potential injection attacks at the rendering boundary.
How should authentication be handled in A2UI applications?
Authentication should follow a two-step model demonstrated in the Personalized Learning sample: first, verify identity using a system like Firebase Authentication, then perform a separate server-side authorization check before accepting UI updates. This separation ensures that the client validates permissions independently of the UI payload, preventing agents from bypassing access controls through manipulated interface descriptions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →