Skip to content

Commit 98b2e0a

Browse files
thompsonmjclaude
andcommitted
Add security tests for manifest deletion path containment
Cover non-string entries, path traversal, absolute paths outside the output directory, and symlink escape. Symlink test skips gracefully when symlink creation is not permitted by the OS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b0b3c62 commit 98b2e0a

1 file changed

Lines changed: 48 additions & 0 deletions

File tree

tests/test_full_rerun.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import json
2+
import os
23

4+
import pytest
35

46
from taxonopy.manifest import (
57
MANIFEST_FILENAMES,
@@ -217,3 +219,49 @@ def test_manifest_written_before_output_files(self, tmp_path):
217219
output_file.write_text("data")
218220
assert manifest_path.exists()
219221
assert output_file.exists()
222+
223+
def test_skips_non_string_entries_in_manifest(self, tmp_path):
224+
(tmp_path / "valid.csv").write_text("data")
225+
files = [42, None, "valid.csv", MANIFEST_FILENAMES["resolve"]]
226+
write_manifest(str(tmp_path), "resolve", "0.2.0", "input/", None, files)
227+
228+
result = delete_from_manifest(str(tmp_path), "resolve")
229+
230+
assert result is True
231+
assert not (tmp_path / "valid.csv").exists()
232+
assert not (tmp_path / MANIFEST_FILENAMES["resolve"]).exists()
233+
234+
def test_rejects_path_traversal_in_manifest(self, tmp_path):
235+
outside = tmp_path.parent / "outside.txt"
236+
outside.write_text("keep me")
237+
files = ["../outside.txt", MANIFEST_FILENAMES["resolve"]]
238+
write_manifest(str(tmp_path), "resolve", "0.2.0", "input/", None, files)
239+
240+
delete_from_manifest(str(tmp_path), "resolve")
241+
242+
assert outside.exists()
243+
244+
def test_rejects_absolute_path_in_manifest(self, tmp_path):
245+
outside = tmp_path.parent / "outside_abs.txt"
246+
outside.write_text("keep me")
247+
files = [str(outside.resolve()), MANIFEST_FILENAMES["resolve"]]
248+
write_manifest(str(tmp_path), "resolve", "0.2.0", "input/", None, files)
249+
250+
delete_from_manifest(str(tmp_path), "resolve")
251+
252+
assert outside.exists()
253+
254+
def test_rejects_symlink_escape_in_manifest(self, tmp_path):
255+
outside = tmp_path.parent / "outside_sym.txt"
256+
outside.write_text("keep me")
257+
link = tmp_path / "link.txt"
258+
try:
259+
link.symlink_to(outside)
260+
except OSError:
261+
pytest.skip("symlink creation not permitted on this platform")
262+
files = ["link.txt", MANIFEST_FILENAMES["resolve"]]
263+
write_manifest(str(tmp_path), "resolve", "0.2.0", "input/", None, files)
264+
265+
delete_from_manifest(str(tmp_path), "resolve")
266+
267+
assert outside.exists()

0 commit comments

Comments
 (0)