ag-ui-protocol / ag-ui-protocol/ag-ui

[BUG]: Client accepts server-asserted message roles; a server-side event chain clears pending interrupts

Abierto
#2,437 3 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Python
Estrellas
15.9k
Forks
1.4k
Merge medio
1 d 17 h
PR fusionados (30 d)
163

Descripción

# 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.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.