import pytest from unittest.mock import patch from pathlib import Path from validations import is_allowed_path, RESTRICTED, RESTRICTED_IN def mock_resolve(self): # Don't modify paths that are from RESTRICTED list initialization if str(self) in [str(p) for p in RESTRICTED]: return self # For symlinks that point to restricted paths, return the target path # without stripping /private/ if str(self).endswith("symlink_restricted"): return Path("/home") # Return the actual restricted target # For other paths, strip /private/ if present return Path(str(self).removeprefix("/private/")) @pytest.mark.parametrize( "test_path, expected", [ # Non-restricted path (should be valid) ("/tmp/somefile", True), # Exactly /mnt (restricted_in) ("/mnt", False), # Exactly / (restricted_in) ("/", False), # Subdirectory inside /mnt/.ix-apps (restricted) ("/mnt/.ix-apps/something", False), # A path that is a restricted directory exactly ("/home", False), ("/var/log", False), ("/mnt/.ix-apps", False), ("/data", False), # Subdirectory inside e.g. /data ("/data/subdir", False), # Not an obviously restricted path ("/usr/local/share", True), # Another system path likely not in restricted list ("/opt/myapp", True), ], ) @patch.object(Path, "resolve", mock_resolve) def test_is_allowed_path_direct(test_path, expected): """Test direct paths against the is_allowed_path function.""" assert is_allowed_path(test_path) == expected @patch.object(Path, "resolve", mock_resolve) def test_is_allowed_path_ix_volume(): """Test that IX volumes are not allowed""" assert is_allowed_path("/mnt/.ix-apps/something", True) @patch.object(Path, "resolve", mock_resolve) def test_is_allowed_path_symlink(tmp_path): """ Test that a symlink pointing to a restricted directory is detected as invalid, and a symlink pointing to an allowed directory is valid. """ # Create a real (allowed) directory and a restricted directory in a temp location allowed_dir = tmp_path / "allowed_dir" allowed_dir.mkdir() restricted_dir = tmp_path / "restricted_dir" restricted_dir.mkdir() # We will simulate that "restricted_dir" is actually a symlink link pointing to e.g. "/var/log" # or we create a subdir to match the restricted pattern. # For demonstration, let's just patch it to a path in the restricted list. real_restricted_path = Path("/home") # This is one of the restricted directories # Create symlinks to test symlink_allowed = tmp_path / "symlink_allowed" symlink_restricted = tmp_path / "symlink_restricted" # Point the symlinks symlink_allowed.symlink_to(allowed_dir) symlink_restricted.symlink_to(real_restricted_path) assert is_allowed_path(str(symlink_allowed)) is True assert is_allowed_path(str(symlink_restricted)) is False def test_is_allowed_path_nested_symlink(tmp_path): """ Test that even a nested symlink that eventually resolves into restricted directories is seen as invalid. """ # e.g., Create 2 symlinks that chain to /root link1 = tmp_path / "link1" link2 = tmp_path / "link2" # link2 -> /root link2.symlink_to(Path("/root")) # link1 -> link2 link1.symlink_to(link2) assert is_allowed_path(str(link1)) is False def test_is_allowed_path_nonexistent(tmp_path): """ Test a path that does not exist at all. The code calls .resolve() which will give the absolute path, but if it's not restricted, it should still be valid. """ nonexistent = tmp_path / "this_does_not_exist" assert is_allowed_path(str(nonexistent)) is True @pytest.mark.parametrize( "test_path", list(RESTRICTED), ) @patch.object(Path, "resolve", mock_resolve) def test_is_allowed_path_restricted_list(test_path): """Test that all items in the RESTRICTED list are invalid.""" assert is_allowed_path(test_path) is False @pytest.mark.parametrize( "test_path", list(RESTRICTED_IN), ) def test_is_allowed_path_restricted_in_list(test_path): """ Test that items in RESTRICTED_IN are invalid. """ assert is_allowed_path(test_path) is False