"""20+ path traversal patterns — all must be rejected.""" import pytest from pyra.security.boundaries import VaultAccessError TRAVERSAL_PATTERNS = [ "../../../../vault/secrets/api_keys.json", "../../../vault/secrets/api_keys.json", "../../vault/secrets/api_keys.json", "../vault/secrets/api_keys.json", "a/b/c/../../../../vault/secrets/api_keys.json", "context/../../vault/secrets/api_keys.json", "user/../../../vault/secrets/api_keys.json", "knowledge/../../../../vault/secrets/api_keys.json", # URL-encoded (resolved by Path.resolve, still blocked) "..%2Fvault%2Fsecrets%2Fapi_keys.json", "%2e%2e/vault/secrets/api_keys.json", # Absolute paths "/etc/passwd", "/root/.ssh/id_rsa", "/tmp/evil", # Home-relative "~/secret", "~/.ssh/id_rsa", # Windows-style (harmless on macOS but should not crash) "..\\vault\\secrets\\api_keys.json", # Double-encoded dot (Path.resolve normalises these) "%252e%252e/vault", # Null bytes in path components (should raise, not silently pass) "valid\x00../../vault", # Extremely deep traversal "a/" * 20 + "../../vault/secrets/api_keys.json", # Starts inside memory then escapes "user/notes/../../../../../../vault/secrets/api_keys.json", ] @pytest.mark.parametrize("name", TRAVERSAL_PATTERNS) def test_memory_read_blocks_traversal(tmp_pyra_home, name): from pyra.memory.reader import read_memory with pytest.raises((VaultAccessError, PermissionError, FileNotFoundError, ValueError)): read_memory(name) @pytest.mark.parametrize("name", TRAVERSAL_PATTERNS) def test_memory_write_blocks_traversal(tmp_pyra_home, name): from pyra.memory.writer import write_memory with pytest.raises((VaultAccessError, PermissionError, FileNotFoundError, ValueError)): write_memory(name, "evil")