Skip to content

Commit 171ae9e

Browse files
adilburaksenxuanyang15
authored andcommitted
fix(agents): prevent path traversal in AgentTool config_path resolution
Merge #5826 ## Summary `resolve_agent_reference` in `config_agent_utils.py` accepted absolute `config_path` values unconditionally and joined relative paths without any boundary validation. An attacker-controlled `config_path` field in an agent YAML could traverse outside the intended agent directory. **Vulnerable pattern (before):** ```python if os.path.isabs(ref_config.config_path): return from_config(ref_config.config_path) # absolute accepted else: return from_config(os.path.join(agent_dir, ref_config.config_path)) # no ".." check ``` **PoC config:** ```yaml tools: - tool_class: AgentTool args: agent: config_path: "../../../../../../etc/passwd" ``` This causes `open("/etc/passwd", "r")` server-side. A `FileNotFoundError` vs `ValidationError` difference also leaks path existence. ## Fix - Reject absolute `config_path` values with `ValueError` - Normalize the joined path and verify it stays within the parent agent directory before calling `from_config` ## Related Same vulnerability exists in `adk-java` (PR: google/adk-java#...) and `adk-go` (PR: google/adk-go#...) — fix pattern is identical. Note: This is distinct from the `resolve_code_reference` RCE (different function, different field, file-read impact vs code execution). Co-authored-by: Xuan Yang <xygoogle@google.com> COPYBARA_INTEGRATE_REVIEW=#5826 from adilburaksen:fix/config-path-traversal 4630cd9 PiperOrigin-RevId: 936810988
1 parent f3529e9 commit 171ae9e

2 files changed

Lines changed: 41 additions & 10 deletions

File tree

src/google/adk/agents/config_agent_utils.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ def _load_config_from_path(config_path: str) -> AgentConfig:
8686
"""Load an agent's configuration from a YAML file.
8787
8888
Args:
89-
config_path: Path to the YAML config file. Both relative and absolute
90-
paths are accepted.
89+
config_path: Path to the YAML config file. Both relative and absolute paths
90+
are accepted.
9191
9292
Returns:
9393
The loaded and validated AgentConfig object.
@@ -124,21 +124,31 @@ def resolve_agent_reference(
124124
Args:
125125
ref_config: The agent reference configuration (AgentRefConfig).
126126
referencing_agent_config_abs_path: The absolute path to the agent config
127-
that contains the reference.
127+
that contains the reference.
128128
129129
Returns:
130130
The created agent instance.
131131
"""
132132
if ref_config.config_path:
133133
if os.path.isabs(ref_config.config_path):
134-
return from_config(ref_config.config_path)
135-
else:
136-
return from_config(
137-
os.path.join(
138-
os.path.dirname(referencing_agent_config_abs_path),
139-
ref_config.config_path,
140-
)
134+
raise ValueError(
135+
"Absolute paths are not allowed in AgentRefConfig config_path:"
136+
f" {ref_config.config_path!r}"
141137
)
138+
agent_dir = os.path.dirname(referencing_agent_config_abs_path)
139+
resolved_path = os.path.realpath(
140+
os.path.join(agent_dir, ref_config.config_path)
141+
)
142+
canonical_agent_dir = os.path.realpath(agent_dir)
143+
if (
144+
os.path.commonpath([canonical_agent_dir, resolved_path])
145+
!= canonical_agent_dir
146+
):
147+
raise ValueError(
148+
f"Path traversal detected: config_path {ref_config.config_path!r}"
149+
" resolves outside the agent directory"
150+
)
151+
return from_config(resolved_path)
142152
elif ref_config.code:
143153
return _resolve_agent_code_reference(ref_config.code)
144154
else:

tests/unittests/agents/test_agent_config.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,3 +465,24 @@ def fake_from_config(path: str):
465465
)
466466
assert result == "sentinel"
467467
assert recorded["path"] == expected_path
468+
469+
470+
def test_resolve_agent_reference_blocks_absolute_path():
471+
"""Verify resolve_agent_reference raises ValueError for absolute paths."""
472+
ref_config = AgentRefConfig(config_path="/etc/passwd")
473+
with pytest.raises(
474+
ValueError,
475+
match="Absolute paths are not allowed in AgentRefConfig config_path",
476+
):
477+
config_agent_utils.resolve_agent_reference(
478+
ref_config, "/workspace/main.yaml"
479+
)
480+
481+
482+
def test_resolve_agent_reference_blocks_path_traversal():
483+
"""Verify resolve_agent_reference raises ValueError for path traversal."""
484+
ref_config = AgentRefConfig(config_path="../outside.yaml")
485+
with pytest.raises(ValueError, match="Path traversal detected"):
486+
config_agent_utils.resolve_agent_reference(
487+
ref_config, "/workspace/agents/main.yaml"
488+
)

0 commit comments

Comments
 (0)