67 lines
2.0 KiB
Python
67 lines
2.0 KiB
Python
import os
|
|
import tempfile
|
|
import pytest
|
|
from pathguard import safe_join, PathTraversalError
|
|
|
|
|
|
def test_basic_join():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
out = safe_join(d, "a", "b.txt")
|
|
assert out == os.path.join(os.path.realpath(d), "a", "b.txt")
|
|
|
|
|
|
def test_dotdot_escape_rejected():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
with pytest.raises(PathTraversalError):
|
|
safe_join(d, "..", "etc", "passwd")
|
|
|
|
|
|
def test_absolute_part_rejected():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
with pytest.raises(PathTraversalError):
|
|
safe_join(d, "/etc/passwd")
|
|
|
|
|
|
def test_symlink_escape_rejected():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
base = os.path.join(d, "base")
|
|
os.makedirs(base)
|
|
target = os.path.join(d, "secret.txt")
|
|
with open(target, "w") as f:
|
|
f.write("secret")
|
|
link = os.path.join(base, "link")
|
|
os.symlink(target, link)
|
|
# reading through link to outside must be rejected when resolved?
|
|
# safe_join resolves; link itself is inside base by name but points outside.
|
|
# Our function resolves symlinks and checks containment -> should reject.
|
|
with pytest.raises(PathTraversalError):
|
|
safe_join(base, "link")
|
|
|
|
|
|
def test_valid_nested():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
sub = os.path.join(d, "sub")
|
|
os.makedirs(sub)
|
|
out = safe_join(d, "sub", "file.txt")
|
|
assert out.startswith(os.path.realpath(d) + os.sep)
|
|
|
|
|
|
def test_empty_parts_ignored():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
out = safe_join(d, "", None, "x")
|
|
assert out.endswith("x")
|
|
|
|
|
|
def test_nul_rejected():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
with pytest.raises(PathTraversalError):
|
|
safe_join(d, "a\x00b")
|
|
|
|
|
|
def test_base_not_dir():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
f = os.path.join(d, "f")
|
|
open(f, "w").close()
|
|
with pytest.raises(PathTraversalError):
|
|
safe_join(f, "x")
|