Project.validate_relative_path: Make handling of ignored files explicit #672

This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2025-10-15 18:16:55 +02:00
1 parent 5f114d9e05
commit 39a11657a9
2 files changed
+14 -10

No files matched your search

+8 -4
View File
@@ -170,18 +170,22 @@ class Project:
abs_path = Path(self.project_root) / relative_path
return abs_path.exists()
def validate_relative_path(self, relative_path: str) -> None:
def validate_relative_path(self, relative_path: str, require_not_ignored: bool = False) -> None:
"""
Validates that the given relative path to an existing file/dir is safe to read or edit,
meaning it's inside the project directory and is not ignored by git.
meaning it's inside the project directory.
Passing a path to a non-existing file will lead to a `FileNotFoundError`.
:param relative_path: the path to validate, relative to the project root
:param require_not_ignored: if True, the path must not be ignored according to the project's ignore settings
"""
if not self.is_path_in_project(relative_path):
raise ValueError(f"{relative_path=} points to path outside of the repository root; cannot access for safety reasons")
if self.is_ignored_path(relative_path):
raise ValueError(f"Path {relative_path} is ignored; cannot access for safety reasons")
if require_not_ignored:
if self.is_ignored_path(relative_path):
raise ValueError(f"Path {relative_path} is ignored; cannot access for safety reasons")
def gather_source_files(self, relative_path: str = "") -> list[str]:
"""Retrieves relative paths of all source files, optionally limited to the given path
+6 -6
View File
@@ -36,7 +36,7 @@ class ReadFileTool(Tool):
required for the task.
:return: the full text of the file at the given relative path
"""
self.project.validate_relative_path(relative_path)
self.project.validate_relative_path(relative_path, require_not_ignored=True)
result = self.project.read_file(relative_path)
result_lines = result.splitlines()
@@ -67,7 +67,7 @@ class CreateTextFileTool(Tool, ToolMarkerCanEdit):
will_overwrite_existing = abs_path.exists()
if will_overwrite_existing:
self.project.validate_relative_path(relative_path)
self.project.validate_relative_path(relative_path, require_not_ignored=True)
else:
assert abs_path.is_relative_to(
self.get_project_root()
@@ -88,7 +88,7 @@ class ListDirTool(Tool):
def apply(self, relative_path: str, recursive: bool, skip_ignored_files: bool = False, max_answer_chars: int = -1) -> str:
"""
Lists all non-gitignored files and directories in the given directory (optionally with recursion).
Lists files and directories in the given directory (optionally with recursion).
:param relative_path: the relative path to the directory to list; pass "." to scan the project root
:param recursive: whether to scan subdirectories recursively
@@ -107,7 +107,7 @@ class ListDirTool(Tool):
}
return json.dumps(error_info)
self.project.validate_relative_path(relative_path)
self.project.validate_relative_path(relative_path, require_not_ignored=skip_ignored_files)
dirs, files = scan_directory(
os.path.join(self.get_project_root(), relative_path),
@@ -134,7 +134,7 @@ class FindFileTool(Tool):
:param relative_path: the relative path to the directory to search in; pass "." to scan the project root
:return: a JSON object with the list of matching files
"""
self.project.validate_relative_path(relative_path)
self.project.validate_relative_path(relative_path, require_not_ignored=True)
dir_to_scan = os.path.join(self.get_project_root(), relative_path)
@@ -192,7 +192,7 @@ class ReplaceRegexTool(Tool, ToolMarkerCanEdit):
If this is set to False and the regex matches multiple occurrences, an error will be returned
(and you may retry with a revised, more specific regex).
"""
self.project.validate_relative_path(relative_path)
self.project.validate_relative_path(relative_path, require_not_ignored=True)
with EditedFileContext(relative_path, self.agent) as context:
original_content = context.get_original_content()
updated_content, n = re.subn(regex, repl, original_content, flags=re.DOTALL | re.MULTILINE)