T09 · Insecure Skill Coding Practices
- Location
modules/git-testing-fixtures.md:36- Finding
Path Traversal in Git Test Repository File Helper
- Content
View full analysis
Path: """Add a file to the repository.""" full_path = self.path / file_path full_path.parent.mkdir(parents=True, exist_ok=True) full_path.write_text(content) return full_path ``` ### Technical Analysis The `add_file()` helper directly combines the repository path with the caller-controlled `file_path` without validating that the resulting destination remains inside the test repository. A path containing parent-directory components, such as `../../target`, can escape `self.path`. In addition, when `file_path` is absolute, `pathlib.Path` discards the preceding repository path and uses the absolute path directly. The subsequent calls to `mkdir()` and `write_text()` then create directories and write attacker-controlled content outside the intended temporary repository. No shell invocation is involved, so this is not command injection. It is an arbitrary file-write flaw constrained by the operating-system permissions of the test process. ### Attack Path 1. An attacker influences a filename passed to `GitRepository.add_file()`, such as through generated test data, an imported fixture, or an untrusted repository test case. 2. The attacker supplies a traversal path such as `../../project-file` or an absolute filesystem path. 3. `self.path / file_path` resolves to a location outside the temporary Git repository. 4. `full_path.parent.mkdir(parents=True, exist_ok=True)` creates any required parent directories. 5. `full_path.write_text(content)` creates or overwrites the selected file with attacker-controlled content. ### Impact Assessment Successful exploitation allows creation or overwrite of any file writable by the account running pytest. In a developer en ...[truncated 439 chars]- Remediation
View remediation
Path: """Add a file only within the test repository.""" relative_path = Path(file_path) if relative_path.is_absolute(): raise ValueError("file_path must be relative") root = self.path.resolve() target = (root / relative_path).resolve() if not target.is_relative_to(root): raise ValueError("file_path must remain inside the repository") target.parent.mkdir(parents=True, exist_ok=True) target.write_text(content) return target ``` Also add tests covering absolute paths, `../` traversal, nested traversal, valid nested paths, and symlink-based escape attempts. If compatibility with Python versions before `Path.is_relative_to()` is required, compare paths using `target.relative_to(root)` and reject `ValueError`. ]]>
