diff --git a/tests/core/test_sanitize.py b/tests/core/test_sanitize.py new file mode 100644 index 00000000..0d1ddeba --- /dev/null +++ b/tests/core/test_sanitize.py @@ -0,0 +1,25 @@ +"""Regression tests for peer-input sanitizers (``core/api/sanitize.py``).""" + +from __future__ import annotations + +from unshackle.core.api.sanitize import safe_cache_key + + +def test_safe_cache_key_rejects_backslash_traversal(): + # On POSIX, backslash is a plain character, so pathlib.Path treats + # "..\\..\\secret" as one filename; written as ".json" it escapes the + # cache dir on Windows. PureWindowsPath handling must reject it everywhere. + assert safe_cache_key("..\\..\\secret") is None + assert safe_cache_key("sub\\dir") is None + + +def test_safe_cache_key_rejects_forward_slash_and_dotdot(): + assert safe_cache_key("../secret") is None + assert safe_cache_key("a/b") is None + assert safe_cache_key("..") is None + assert safe_cache_key(".") is None + assert safe_cache_key("") is None + + +def test_safe_cache_key_accepts_plain_filename(): + assert safe_cache_key("valid-key_123") == "valid-key_123" diff --git a/unshackle/core/api/sanitize.py b/unshackle/core/api/sanitize.py index f62bcf33..fd516db0 100644 --- a/unshackle/core/api/sanitize.py +++ b/unshackle/core/api/sanitize.py @@ -2,7 +2,7 @@ from __future__ import annotations -from pathlib import Path +from pathlib import PureWindowsPath from typing import Optional @@ -17,9 +17,12 @@ def safe_cache_key(key: object) -> Optional[str]: Cache keys are written as ``.json`` inside a fixed directory. A key containing a path separator, ``..``, or an absolute path would let the peer write outside that directory, so anything that is not already a plain filename is rejected. + + PureWindowsPath treats both ``/`` and ``\\`` as separators, so a key like + ``..\\..\\secret`` is rejected even on POSIX (where backslash is a plain character). """ text = str(key) - name = Path(text).name + name = PureWindowsPath(text).name if not name or name != text or name in {".", ".."}: return None return name