ag-ui-protocol / ag-ui-protocol/ag-ui
[BUG]: Client accepts server-asserted message roles; a server-side event chain clears pending interrupts
- Vorherrschende Sprache
- Python
- Sterne
- 15.9k
- Forks
- 1.4k
- Ø Merge
- 1 T. 17 Std.
- Gemergte PRs (30 T.)
- 163
Beschreibung
# Client accepts server-asserted message roles; a server-side event chain clears pending interrupts
Repository: https://github.com/ag-ui-protocol/ag-ui
Affected: `@ag-ui/client` (event application layer)
CWE: CWE-346 (Origin Validation Error)
## Summary
The client's event application layer trusts the server stream for information that originates from the user side:
1. `TEXT_MESSAGE_START` carries any role, including `user`; the message is stored as-is (`apply/default.ts:168-186`) and returned to the server in the next request's `messages` (`agent/agent.ts:385-399`, `prepareRunAgentInput`).
2. `MESSAGES_SNAPSHOT` replaces the entire history with entries in any role, including `developer`/`system` (`apply/default.ts:626-679`).
3. Any `RUN_FINISHED` clears `pendingInterrupts` (`apply/default.ts:924-927`): a stream emitting `RUN_FINISHED(interrupt) → RUN_STARTED → RUN_FINISHED(success)` leaves no pending interrupt, so the next `runAgent()` proceeds without any resume — bypassing the guard at `agent.ts:402-417`.
## Observed behavior (published `@ag-ui/client` against a local SSE server)
- A forged `role: "user"` message ("I approve the transfer of $10,000…") and a forged `developer` message ("SYSTEM OVERRIDE…") were both stored and sent back verbatim in the next request body.
- After the server-side interrupt→success chain, `agent.pendingInterrupts` was empty and the next `runAgent()` succeeded with no `resume` field in the request.
## Impact
The client-side conversation record cannot distinguish server-authored entries from genuine user input, and human-in-the-loop gates implemented on top of `pendingInterrupts` can be released by the server itself. In single-backend deployments the backend is already the authority over its own tools, so the practical gap is: client-side approval flows, multi-hop/proxy topologies where the endpoint is not the sole authority, audit logs that treat roles as authentic, and UI display of fabricated "user said" content.
## Suggested remediation
- Track message provenance (client-authored vs. server-streamed) and exclude or flag server-streamed `user`/`developer` entries when building the next request.
- Only an explicit client resume should clear `pendingInterrupts`; a server-emitted success `RUN_FINISHED` after an interrupt should not.
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.