Skip to content

security: reject caller writes to reserved internal session state keys#1242

Open
XananasX7 wants to merge 1 commit into
google:mainfrom
XananasX7:fix/reserved-session-state-keys
Open

security: reject caller writes to reserved internal session state keys#1242
XananasX7 wants to merge 1 commit into
google:mainfrom
XananasX7:fix/reserved-session-state-keys

Conversation

@XananasX7
Copy link
Copy Markdown

Summary

The dev web API accepts arbitrary session state from callers through both initial session creation (POST /apps/{app}/users/{user}/sessions) and per-run stateDelta (POST /run / POST /run_sse). Internal framework keys such as _code_execution_context are stored in the same mutable session-state namespace, with no restriction on caller writes.

Root cause

InMemorySessionService.createSession() accepts a caller-supplied Map<String, Object> state and stores it verbatim. appendEvent() iterates stateDelta and writes every non-prefixed key directly to session.state().

CodeExecution.getOrSetExecutionId() reads _code_execution_context.execution_session_id from session state, and returns the existing value if one is already present without any validation. VertexAiCodeExecutor then forwards that value as session_id to the Vertex code interpreter extension.

Combined, a caller can:

{
  "state": {
    "_code_execution_context": {
      "execution_session_id": "attacker-chosen-session"
    }
  }
}

…and force subsequent code execution to bind to an arbitrary interpreter session identifier instead of the one derived from the ADK session.

Fix

Added a RESERVED_STATE_KEYS constant enumerating all internal executor-control keys:

private static final Set<String> RESERVED_STATE_KEYS =
    ImmutableSet.of(
        "_code_execution_context",
        "_code_executor_input_files",
        "_code_executor_error_counts",
        "_code_execution_results");

Added sanitizeCallerState() which strips any reserved key from a caller-supplied state map (logging a warning for each):

  • Applied in createSession() so reserved keys in initial session state are never persisted.
  • Applied in appendEvent() so per-run stateDelta cannot overwrite reserved keys either.

Internal framework code that legitimately writes these keys does so through CodeExecutorContext.getStateDelta()appendEvent() — but those writes go through the EventActions.stateDelta() path which is generated server-side, not from untrusted caller input.

The dev web API accepted arbitrary session state from callers through
both initial session creation and per-run stateDelta. Internal framework
keys such as _code_execution_context are stored in the same mutable
session-state namespace that callers can write to.

The code execution path in CodeExecution.getOrSetExecutionId() reads
_code_execution_context.execution_session_id from session state and
prefers any pre-existing value over deriving a new one from the ADK
session id. This means a caller who seeds that key via createSession
state or stateDelta can steer which backing interpreter session is used
by VertexAiCodeExecutor, potentially accessing another session's files,
variables, or prior execution context.

Fix:
- Add RESERVED_STATE_KEYS constant enumerating all internal executor
  control keys (_code_execution_context, _code_executor_input_files,
  _code_executor_error_counts, _code_execution_results).
- Add sanitizeCallerState() helper that strips reserved keys from any
  caller-supplied state map and logs a warning for each dropped key.
- Apply sanitizeCallerState() in createSession() so reserved keys in
  initial session state are never persisted.
- In appendEvent(), skip writes for reserved keys in stateDelta with a
  warning log, so per-run stateDelta cannot overwrite them either.

Signed-off-by: El Mehdi Abenhazou <mehdiananas007@gmail.com>
@XananasX7
Copy link
Copy Markdown
Author

Hi — this fixes a caller-controlled session state injection reported to Google OSS VRP. The dev web API accepts arbitrary state through both session creation and per-run stateDelta, with no restriction on reserved internal keys like _code_execution_context. Since CodeExecution.getOrSetExecutionId() prefers any pre-existing value in session state, a caller can steer which interpreter session VertexAiCodeExecutor uses.

The fix adds a RESERVED_STATE_KEYS blocklist and sanitizeCallerState() helper that strips reserved keys from any caller-supplied state, applied at both createSession() and appendEvent(). Happy to discuss or refine the approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant