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

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

オープン
#2,437 コメント 3 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
Python
スター
15.9k
フォーク
1.4k
平均マージ
1日 17時間
マージ済み PR(30日)
163

説明

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

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。