Skip to content

Commit e6c24ce

Browse files
committed
Fix path traversal in LocalEnvironment._resolve_path
Add containment check using pathlib.Path.resolve() and relative_to() to prevent directory traversal attacks. Previously, absolute paths were returned without validation, and relative paths using ../ could escape the workspace directory boundary. Fixes #5869
1 parent 4006fe4 commit e6c24ce

1 file changed

Lines changed: 15 additions & 5 deletions

File tree

src/google/adk/environment/_local_environment.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import logging
2121
import os
2222
from pathlib import Path
23+
from pathlib import Path as PathLib
2324
import shutil
2425
import tempfile
2526
from typing import Optional
@@ -139,11 +140,20 @@ async def write_file(self, path: str | Path, content: str | bytes) -> None:
139140
return await asyncio.to_thread(self._sync_write, resolved, content)
140141

141142
def _resolve_path(self, path: str | Path) -> str:
142-
"""Resolve a relative path against the working directory."""
143-
path = str(path)
144-
if os.path.isabs(path):
145-
return path
146-
return os.path.join(self._working_dir, path)
143+
"""Resolve path against working directory and check for directory traversal."""
144+
working_dir = PathLib(self._working_dir).resolve()
145+
target = PathLib(str(path))
146+
if not target.is_absolute():
147+
target = working_dir / target
148+
resolved_target = target.resolve()
149+
try:
150+
resolved_target.relative_to(working_dir)
151+
except ValueError:
152+
raise PermissionError(
153+
f"Access denied: path '{path}' resolves outside the workspace"
154+
f" directory '{working_dir}'."
155+
)
156+
return str(resolved_target)
147157

148158
@staticmethod
149159
def _sync_read(path: str) -> bytes:

0 commit comments

Comments
 (0)