Skip to content

Commit 74f235b

Browse files
sasha-gitgcopybara-github
authored andcommitted
fix: block RCE vulnerability via nested YAML configurations in ADK
Co-authored-by: Sasha Sobran <asobran@google.com> PiperOrigin-RevId: 902898232
1 parent 6380f6a commit 74f235b

3 files changed

Lines changed: 54 additions & 0 deletions

File tree

src/google/adk/agents/config_agent_utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,31 @@ def _resolve_agent_class(agent_class: str) -> type[BaseAgent]:
8080
)
8181

8282

83+
_BLOCKED_YAML_KEYS = frozenset({"args"})
84+
_ENFORCE_DENYLIST = False
85+
86+
87+
def _set_enforce_denylist(value: bool) -> None:
88+
global _ENFORCE_DENYLIST
89+
_ENFORCE_DENYLIST = value
90+
91+
92+
def _check_config_for_blocked_keys(node: Any, filename: str) -> None:
93+
"""Recursively check if the configuration contains any blocked keys."""
94+
if isinstance(node, dict):
95+
for key, value in node.items():
96+
if key in _BLOCKED_YAML_KEYS:
97+
raise ValueError(
98+
f"Blocked key {key!r} found in {filename!r}. "
99+
f"The '{key}' field is not allowed in agent configurations "
100+
"because it can execute arbitrary code."
101+
)
102+
_check_config_for_blocked_keys(value, filename)
103+
elif isinstance(node, list):
104+
for item in node:
105+
_check_config_for_blocked_keys(item, filename)
106+
107+
83108
def _load_config_from_path(config_path: str) -> AgentConfig:
84109
"""Load an agent's configuration from a YAML file.
85110
@@ -100,6 +125,9 @@ def _load_config_from_path(config_path: str) -> AgentConfig:
100125
with open(config_path, "r", encoding="utf-8") as f:
101126
config_data = yaml.safe_load(f)
102127

128+
if _ENFORCE_DENYLIST:
129+
_check_config_for_blocked_keys(config_data, config_path)
130+
103131
return AgentConfig.model_validate(config_data)
104132

105133

src/google/adk/cli/fast_api.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ def get_fast_api_app(
148148
The configured FastAPI application instance.
149149
"""
150150

151+
# Enable denylist enforcement for config loads if web UI is enabled.
152+
if web:
153+
from ..agents import config_agent_utils
154+
155+
config_agent_utils._set_enforce_denylist(True)
156+
151157
# Set up eval managers.
152158
if eval_storage_uri:
153159
gcs_eval_managers = evals.create_gcs_eval_managers_from_uri(

tests/unittests/agents/test_agent_config.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,23 @@ def fake_from_config(path: str):
421421
)
422422
assert result == "sentinel"
423423
assert recorded["path"] == expected_path
424+
425+
426+
def test_load_config_from_path_blocks_args_when_enforced(tmp_path):
427+
"""Verify _load_config_from_path blocks 'args' when enforcement is enabled."""
428+
config_file = tmp_path / "malicious.yaml"
429+
config_file.write_text("""
430+
name: malicious_agent
431+
tools:
432+
- name: some_tool
433+
args:
434+
cmd: "rm -rf /"
435+
""")
436+
437+
config_agent_utils._set_enforce_denylist(True)
438+
try:
439+
with pytest.raises(ValueError) as exc_info:
440+
config_agent_utils._load_config_from_path(str(config_file))
441+
assert "Blocked key 'args' found" in str(exc_info.value)
442+
finally:
443+
config_agent_utils._set_enforce_denylist(False)

0 commit comments

Comments
 (0)