|
1 | 1 | import json |
| 2 | +import pytest |
2 | 3 | from unittest.mock import patch |
3 | 4 |
|
4 | 5 | from mini_coding_agent import ( |
@@ -188,6 +189,41 @@ def test_list_files_hides_internal_agent_state(tmp_path): |
188 | 189 | assert "[F] hello.txt" in result |
189 | 190 |
|
190 | 191 |
|
| 192 | +def test_path_rejects_parent_escape(tmp_path): |
| 193 | + agent = build_agent(tmp_path, []) |
| 194 | + |
| 195 | + with pytest.raises(ValueError, match="path escapes workspace"): |
| 196 | + agent.path("../outside.txt") |
| 197 | + |
| 198 | + |
| 199 | +def test_path_rejects_symlink_escape(tmp_path): |
| 200 | + agent = build_agent(tmp_path, []) |
| 201 | + outside = tmp_path.parent / f"{tmp_path.name}-outside" |
| 202 | + outside.mkdir() |
| 203 | + link = tmp_path / "outside-link" |
| 204 | + try: |
| 205 | + link.symlink_to(outside, target_is_directory=True) |
| 206 | + except (OSError, NotImplementedError): |
| 207 | + pytest.skip("symlink creation is not available in this environment") |
| 208 | + |
| 209 | + with pytest.raises(ValueError, match="path escapes workspace"): |
| 210 | + agent.path("outside-link/secret.txt") |
| 211 | + |
| 212 | + |
| 213 | +def test_path_accepts_case_variant_on_case_insensitive_filesystems(tmp_path): |
| 214 | + project_root = tmp_path / "Proj" |
| 215 | + project_root.mkdir() |
| 216 | + agent = build_agent(project_root, []) |
| 217 | + variant = project_root.parent / project_root.name.lower() / "README.md" |
| 218 | + |
| 219 | + if not variant.exists(): |
| 220 | + pytest.skip("case-sensitive filesystem") |
| 221 | + |
| 222 | + resolved = agent.path(str(variant)) |
| 223 | + |
| 224 | + assert resolved.samefile(project_root / "README.md") |
| 225 | + |
| 226 | + |
191 | 227 | def test_repeated_identical_tool_call_is_rejected(tmp_path): |
192 | 228 | agent = build_agent(tmp_path, []) |
193 | 229 | agent.record({"role": "tool", "name": "list_files", "args": {}, "content": "(empty)", "created_at": "1"}) |
|
0 commit comments