From 1274b091d1965060d622b6f3e54e7ba5fc26e675 Mon Sep 17 00:00:00 2001 From: adilburaksen Date: Sun, 24 May 2026 00:38:09 +0300 Subject: [PATCH 1/2] fix(agents): prevent path traversal in AgentTool config_path resolution Absolute config_path values were accepted unconditionally, and relative paths were joined without boundary validation, allowing traversal outside the agent directory via "../../../etc/passwd" style inputs. Fix: reject absolute paths; for relative paths, verify the normalized result stays within the parent agent's directory before loading. --- src/google/adk/agents/config_agent_utils.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/google/adk/agents/config_agent_utils.py b/src/google/adk/agents/config_agent_utils.py index f9a3e7f594..9dc3f732f9 100644 --- a/src/google/adk/agents/config_agent_utils.py +++ b/src/google/adk/agents/config_agent_utils.py @@ -157,14 +157,21 @@ def resolve_agent_reference( """ if ref_config.config_path: if os.path.isabs(ref_config.config_path): - return from_config(ref_config.config_path) - else: - return from_config( - os.path.join( - os.path.dirname(referencing_agent_config_abs_path), - ref_config.config_path, - ) + raise ValueError( + f"Absolute paths are not allowed in AgentTool config_path:" + f" {ref_config.config_path!r}" ) + agent_dir = os.path.dirname(referencing_agent_config_abs_path) + resolved_path = os.path.normpath( + os.path.join(agent_dir, ref_config.config_path) + ) + canonical_agent_dir = os.path.normpath(agent_dir) + if not resolved_path.startswith(canonical_agent_dir + os.sep): + raise ValueError( + f"Path traversal detected: config_path {ref_config.config_path!r}" + " resolves outside the agent directory" + ) + return from_config(resolved_path) elif ref_config.code: return _resolve_agent_code_reference(ref_config.code) else: From 8be0661d74f32427dab05d334b8e202207651f4c Mon Sep 17 00:00:00 2001 From: adilburaksen Date: Wed, 27 May 2026 21:19:30 +0300 Subject: [PATCH 2/2] fix: use os.path.sep for cross-platform boundary check + pre-commit fixes Replace os.sep with os.path.sep in the traversal boundary check so the check works correctly when os.path is replaced with ntpath (Windows paths). os.sep is not affected by patching os.path; os.path.sep is. Also fix pre-commit issues from upstream merge: end-of-file newlines, trailing whitespace, and isort import ordering. --- .github/workflows/check-file-contents.yml | 2 +- src/google/adk/agents/config_agent_utils.py | 4 ++-- src/google/adk/cli/utils/evals.py | 3 ++- tests/unittests/skills/test__utils.py | 2 -- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check-file-contents.yml b/.github/workflows/check-file-contents.yml index a6c31788fa..42d820ab47 100644 --- a/.github/workflows/check-file-contents.yml +++ b/.github/workflows/check-file-contents.yml @@ -111,7 +111,7 @@ jobs: # 1. Identify files containing any googleapis.com URL. set +e FILES_WITH_ENDPOINTS=$(grep -lE 'https?://[a-zA-Z0-9.-]+\.googleapis\.com' $CHANGED_FILES) - + # 2. From those, identify files that are MISSING the required mTLS version. if [ -n "$FILES_WITH_ENDPOINTS" ]; then FILES_MISSING_MTLS=$(grep -L '.mtls.googleapis.com' $FILES_WITH_ENDPOINTS) diff --git a/src/google/adk/agents/config_agent_utils.py b/src/google/adk/agents/config_agent_utils.py index c85cdbf3b1..62b8f9a39a 100644 --- a/src/google/adk/agents/config_agent_utils.py +++ b/src/google/adk/agents/config_agent_utils.py @@ -539,7 +539,7 @@ def resolve_agent_reference( if ref_config.config_path: if os.path.isabs(ref_config.config_path): raise ValueError( - f"Absolute paths are not allowed in AgentTool config_path:" + "Absolute paths are not allowed in AgentTool config_path:" f" {ref_config.config_path!r}" ) agent_dir = os.path.dirname(referencing_agent_config_abs_path) @@ -547,7 +547,7 @@ def resolve_agent_reference( os.path.join(agent_dir, ref_config.config_path) ) canonical_agent_dir = os.path.normpath(agent_dir) - if not resolved_path.startswith(canonical_agent_dir + os.sep): + if not resolved_path.startswith(canonical_agent_dir + os.path.sep): raise ValueError( f"Path traversal detected: config_path {ref_config.config_path!r}" " resolves outside the agent directory" diff --git a/src/google/adk/cli/utils/evals.py b/src/google/adk/cli/utils/evals.py index d2ca8878f3..3f4085cba0 100644 --- a/src/google/adk/cli/utils/evals.py +++ b/src/google/adk/cli/utils/evals.py @@ -15,7 +15,8 @@ from __future__ import annotations import os -from typing import Any, TYPE_CHECKING +from typing import Any +from typing import TYPE_CHECKING from pydantic import alias_generators from pydantic import BaseModel diff --git a/tests/unittests/skills/test__utils.py b/tests/unittests/skills/test__utils.py index 0547c630a5..abae9cd8b8 100644 --- a/tests/unittests/skills/test__utils.py +++ b/tests/unittests/skills/test__utils.py @@ -393,5 +393,3 @@ def mock_import(name, globals=None, locals=None, fromlist=(), level=0): with mock.patch("builtins.__import__", mock_import): with pytest.raises(ImportError, match="google-cloud-storage is required"): _load_skill_from_gcs_dir("my-bucket", "skills/my-skill/") - -