Repository navigation
[BREAKING] Python: Harden AG-UI trust boundaries - #9316
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Approval-key collisions, stale checkpoint ownership, incomplete A2UI gating, and sensitive INFO logging leave security issues unresolved.
6 open findings
Prevent scoped and unscoped approval key collisions · New Read build-time storage instead of stale runtime override · New Gate optional A2UI import on backend opt-in · New Remove sensitive approval arguments from INFO logs · New Add streaming invalidation regression coverage · New Test streaming completion without TOOL_CALL_END · New
What changed in this PR
Hardens Python AG-UI boundaries around client-controlled state, capabilities, approvals, checkpoints, and multimodal content.
Changes:
- Enforces checkpoint ownership and safer approval identity scoping.
- Treats state/A2UI context as untrusted and restricts capability activation and URI schemes.
- Adds response invalidation handling and reduces sensitive INFO logging.
| File | Description |
|---|---|
python/packages/ag-ui/AGENTS.md |
Documents hardened security contracts. |
agent_framework_ag_ui/_workflow.py |
Adds owned checkpoint filtering. |
agent_framework_ag_ui/_message_adapters.py |
Validates URIs and reduces approval logging. |
agent_framework_ag_ui/_endpoint.py |
Removes event payloads from INFO logs. |
agent_framework_ag_ui/_client.py |
Handles invalidated client-tool calls. |
agent_framework_ag_ui/_approval_state.py |
Introduces length-prefixed approval keys. |
agent_framework_ag_ui/_agent_run.py |
Injects state as untrusted user data. |
agent_framework_ag_ui/_a2ui/_factory.py |
Requires backend A2UI opt-in. |
agent_framework_ag_ui/_a2ui/_agent.py |
Downgrades client context authority. |
tests/ag_ui/test_run.py |
Updates state and approval tests. |
tests/ag_ui/test_message_adapters.py |
Tests rejected URI schemes. |
tests/ag_ui/test_endpoint.py |
Tests ownership and A2UI configuration. |
tests/ag_ui/test_approval_state.py |
Tests approval-key validation. |
tests/ag_ui/test_ag_ui_client.py |
Tests invalidation and marker-independent completion. |
tests/ag_ui/test_a2ui.py |
Tests untrusted context and opt-in behavior. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Code Coverage OverviewLanguages: Python Python / code-coverage/pythonThe overall line coverage in commit db74497 in the Show a line coverage summary of the most impacted files.
Updated |
There was a problem hiding this comment.
MAF Automated Review — Iteration 1
Result: Findings reported
Scope: full PR (2 commit(s)): 62e4c56b9170, 911089ad2ef8
Model: gpt-5.6-sol
Overview
The PR adds meaningful fail-closed checks for checkpoint ownership, backend A2UI opt-in, untrusted state/context roles, local-tool response invalidation, and multimodal URI schemes, with focused regression coverage. The approval-key encoding is still non-injective across its scoped and unscoped branches, and the URI restriction makes persisted threads containing previously accepted URI schemes fail on every later turn. Those residual issues leave one approval-state isolation gap and one upgrade-time availability regression.
Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
2 verified findings remained after source verification (1 high, 1 medium) across 2 files. Details are attached to the affected lines below.
Affected areas: python/packages/ag-ui/agent_framework_ag_ui/_approval_state.py, python/packages/ag-ui/agent_framework_ag_ui/_message_adapters.py
There was a problem hiding this comment.
MAF Automated Review — Iteration 2
Result: Findings reported
Scope: 2 net-new commit(s): 71ee9f05ec83, 8739953b17c1
Model: gpt-5.6-sol
Overview
This incremental update closes the prior approval-key, stale checkpoint-storage, optional A2UI import, logging, streaming, and legacy-media review gaps with focused guards and regression coverage. The new legacy-snapshot sanitizer deep-copies stored history and safely replaces ordinary unsupported schemes, but malformed URL authority syntax can still escape its parser and make an affected persisted thread unusable.
Reviewed the supplied incremental change set across correctness, security/reliability, architecture, and failure behavior.
1 verified finding remained after source verification (1 medium) across 1 file. Details are attached to the affected lines below.
Affected areas: python/packages/ag-ui/agent_framework_ag_ui/_message_adapters.py
Jose Alvarez (jpalvarezl)
left a comment
There was a problem hiding this comment.
One security-boundary issue remains inline. Separately, this is a non-trivial breaking change, but ### Related Issue is only N/A while the checklist says the PR links an agreed issue or documents a valid exception. Please link the agreed tracking item, or document the applicable private-security/process exception without exposing sensitive details, and make the checklist match.
|
Jose Alvarez (@jpalvarezl) Thanks — both points are addressed. The multimodal boundary fix is in |
…clean # Conflicts: # python/packages/ag-ui/agent_framework_ag_ui/_workflow.py
Jose Alvarez (jpalvarezl)
left a comment
There was a problem hiding this comment.
Re-reviewed the current head. The unresolved id/file media-reference path now fails before constructing provider-facing URI content, legacy snapshots sanitize those references before replay, and the regression coverage includes fresh and persisted forms. No further substantive issues found.



Motivation & Context
AG-UI accepts client-controlled thread state, protocol events, A2UI context, approval identities, and multimodal inputs across several server-authority boundaries. This change centralizes fail-closed handling at those boundaries so request data cannot select another workflow continuation, activate backend capabilities without opt-in, gain system-message authority, or expose sensitive payloads through default logging.
Description & Review Guide
forwardedProps.injectA2UIToolcan activate A2UI.RUN_ERRORafter an exposed local client-tool call toResponseInvalidatedException, while preserving provider-independent call completion semantics without requiringTOOL_CALL_END.id/filemedia references, migrate unsupported media in backend-loaded legacy snapshots to inert placeholders, and remove tool/run and approval payloads from default INFO logging.forwardedProps.injectA2UIToolis now only a per-request selection signal. The backend must explicitly enable the capability witha2ui_config={"inject_a2ui_tool": true}. Manualenable_a2ui(...)integrations are unchanged.http,https, anddataschemes or inline binary data. Unresolved client-ownedid/filereferences are rejected. Applications using file, FTP, relative, custom schemes, or protocol-level binary identifiers must fetch and authorize that resource in application code and pass HTTPS, data, or inline content to AG-UI. Previously persisted Thread Snapshots remain usable: unsupported legacy media parts are replaced with inert placeholder text before hydration or provider replay and are removed when the thread is next persisted.TOOL_CALL_ENDor finish markers. If AG-UI emitsRUN_ERRORafter exposing a local tool call, the response is invalidated and the call is discarded.WorkflowBuilderstorage.ResponseInvalidatedExceptionbehavior for AG-UIRUN_ERROR.Related Issue
N/A - private security remediation tracked through the restricted Microsoft security-response process. This public PR intentionally does not link the restricted tracking record or expose its details.
AI Assistance
AI assistance details: Implementation, tests, security-boundary analysis, documentation, and this PR description were AI-assisted.
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.