Skip to content

Commit 6360b09

Browse files
committed
fix(tools): report EditFile path escapes as tool errors, not crashes
EditFileTool caught only FileNotFoundError around the environment read, so the resolver's ValueError for a path escaping the working directory propagated and failed the whole invocation, while ReadFile and WriteFile already returned it to the model as a recoverable error dict. Catch ValueError alongside FileNotFoundError. This also fixes the same gap for LocalEnvironment, whose resolver has always raised here. Review follow-up to #7700.
1 parent 814ad2e commit 6360b09

2 files changed

Lines changed: 30 additions & 0 deletions

File tree

‎src/google/adk/tools/environment/_edit_file_tool.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ async def run_async(
104104
content = data_bytes.decode('utf-8', errors='replace')
105105
except FileNotFoundError:
106106
return {'status': 'error', 'error': f'File not found: {path}'}
107+
except ValueError as e:
108+
return {'status': 'error', 'error': str(e)}
107109

108110
# Normalize line breaks in old_string to \n and use regex for flexible matching
109111
normalized_old = old_string.replace('\r\n', '\n')

‎tests/unittests/tools/environment/test_edit_file_tool.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,31 @@ async def test_edit_file_handles_special_regex_chars(
148148
assert result["status"] == "ok"
149149
data = await env.read_file("test.txt")
150150
assert data == b"replaced\nline2"
151+
152+
@pytest.mark.asyncio
153+
async def test_edit_file_reports_path_escape_as_tool_error(
154+
self, env: LocalEnvironment
155+
):
156+
"""A `..` path escaping the working dir returns an error, not a raise.
157+
158+
The environment resolver raises ValueError for such paths; ReadFile and
159+
WriteFile already surface it to the model as a recoverable error dict.
160+
"""
161+
# Arrange
162+
tool = EditFileTool(env)
163+
await env.write_file("inside.txt", "line1\nline2")
164+
165+
args = {
166+
"path": "../outside.txt",
167+
"old_string": "line1",
168+
"new_string": "changed",
169+
}
170+
171+
# Act
172+
result = await tool.run_async(args=args, tool_context=None)
173+
174+
# Assert
175+
assert result == {
176+
"status": "error",
177+
"error": "Path escapes working directory: ../outside.txt",
178+
}

0 commit comments

Comments
 (0)