Pre-flight Checklist
Describe the Bug
ADKAgent.run persists the client's RunAgentInput.state into the ADK session, stripping only _INTERNAL_STATE_KEYS. ADK's extract_state_delta then routes keys prefixed app: into app-scoped state, which is merged into every user's sessions for that app_name, and keys prefixed user: into user-scoped state.
So any caller of an add_adk_fastapi_endpoint endpoint, including an authenticated end user sending a normal run, can write state that:
- appears in every other user's session state,
- is sent to other users' browsers in
STATE_SNAPSHOT,
- is returned by
/agents/state.
Anything an app reads from app: state (config, feature flags, {app:key} instruction templates) becomes attacker-controlled. App state is also loaded with every session, so it doubles as a storage/performance vector.
Steps to Reproduce
sessions = DatabaseSessionService("sqlite+aiosqlite:///poc.db") # also InMemorySessionService
agent = ADKAgent(adk_agent=<any agent>, app_name="app",
user_id_extractor=lambda i: i.state["uid"], session_service=sessions)
# 1. User A sends an ordinary run with extra state keys
async for _ in agent.run(RunAgentInput(thread_id="a", run_id="1",
state={"uid": "mallory", "app:contact_email": "phish@evil.example"},
messages=[UserMessage(id="m1", role="user", content="hi")],
tools=[], context=[], forwarded_props={})):
pass
# 2. A different user
alice = await sessions.create_session(app_name="app", user_id="alice")
print(alice.state["app:contact_email"]) # -> phish@evil.example
- Alice's own next run also emits a
STATE_SNAPSHOT containing app:contact_email.
Expected Behavior
Client-supplied state should not be able to write server-scoped ADK state. Keys starting with State.APP_PREFIX / State.USER_PREFIX should be dropped from input.state, next to the existing _INTERNAL_STATE_KEYS strip (the same class of fix as #1168). An explicit opt-in could keep them for apps that deliberately want client-writable app/user state.
Environment
ag-ui-adk 0.7.0 (also 0.6.5), google-adk 2.2.0
Python 3.14
Session services: DatabaseSessionService (sqlite/Postgres), InMemorySessionService
Screenshots
No response
Logs & Errors
Additional Context
No response
Pre-flight Checklist
Describe the Bug
ADKAgent.runpersists the client'sRunAgentInput.stateinto the ADK session, stripping only_INTERNAL_STATE_KEYS. ADK'sextract_state_deltathen routes keys prefixedapp:into app-scoped state, which is merged into every user's sessions for thatapp_name, and keys prefixeduser:into user-scoped state.So any caller of an
add_adk_fastapi_endpointendpoint, including an authenticated end user sending a normal run, can write state that:STATE_SNAPSHOT,/agents/state.Anything an app reads from
app:state (config, feature flags,{app:key}instruction templates) becomes attacker-controlled. App state is also loaded with every session, so it doubles as a storage/performance vector.Steps to Reproduce
STATE_SNAPSHOTcontainingapp:contact_email.Expected Behavior
Client-supplied state should not be able to write server-scoped ADK state. Keys starting with
State.APP_PREFIX/State.USER_PREFIXshould be dropped frominput.state, next to the existing_INTERNAL_STATE_KEYSstrip (the same class of fix as #1168). An explicit opt-in could keep them for apps that deliberately want client-writable app/user state.Environment
Screenshots
No response
Logs & Errors
Additional Context
No response