Skip to content

Commit e506fa6

Browse files
GWealecopybara-github
authored andcommitted
fix: enforce agent-config args denylist when loaded under adk web
Re-enable the config-load denylist (gated on the web UI) so agent configs containing the `args` key are rejected when served via `adk web`, restoring the defense-in-depth layer that complements the existing builder-upload check. The load-time enforcement was inadvertently dropped in an earlier config-wiring refactor. Co-authored-by: George Weale <gweale@google.com> PiperOrigin-RevId: 938283376
1 parent 40a0279 commit e506fa6

3 files changed

Lines changed: 47 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
@@ -82,6 +82,31 @@ def _resolve_agent_class(agent_class: str) -> type[BaseAgent]:
8282
)
8383

8484

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

130+
if _ENFORCE_YAML_KEY_DENYLIST:
131+
_check_config_for_blocked_keys(config_data, config_path)
132+
105133
return AgentConfig.model_validate(config_data)
106134

107135

src/google/adk/cli/fast_api.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,12 @@ def get_fast_api_app(
486486
The configured FastAPI application instance.
487487
"""
488488

489+
# Enable the YAML key denylist for config loads if the web UI is enabled.
490+
if web:
491+
from ..agents import config_agent_utils
492+
493+
config_agent_utils._set_enforce_yaml_key_denylist(True)
494+
489495
# Detect single agent mode
490496
agents_path = Path(agents_dir).resolve()
491497
is_single_agent = is_single_agent_directory(agents_path)

tests/unittests/agents/test_agent_config.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,3 +603,16 @@ def test_denylist_can_be_disabled():
603603
assert callable(result)
604604
finally:
605605
config_agent_utils._set_enforce_denylist(True)
606+
607+
608+
def test_load_config_from_path_blocks_args_when_enforced(tmp_path: Path):
609+
"""_load_config_from_path blocks the 'args' key when enforcement is on."""
610+
config_file = tmp_path / "agent.yaml"
611+
config_file.write_text("name: my_agent\nargs:\n key: value\n")
612+
config_agent_utils._set_enforce_yaml_key_denylist(True)
613+
try:
614+
with pytest.raises(ValueError) as exc_info:
615+
config_agent_utils._load_config_from_path(str(config_file))
616+
assert "Blocked key 'args' found" in str(exc_info.value)
617+
finally:
618+
config_agent_utils._set_enforce_yaml_key_denylist(False)

0 commit comments

Comments
 (0)