From 0d228f4090a9c743a486a653fa11829e60df615c Mon Sep 17 00:00:00 2001 From: stephantul Date: Thu, 4 Jun 2026 13:01:27 +0200 Subject: [PATCH 1/2] fix: disallow relative paths in user-supplied cache dir --- src/semble/cache.py | 20 ++++++++++++++++++-- tests/test_cache.py | 7 +++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/semble/cache.py b/src/semble/cache.py index e424540..b979ed6 100644 --- a/src/semble/cache.py +++ b/src/semble/cache.py @@ -1,5 +1,6 @@ import hashlib import json +import logging import os import shutil import sys @@ -13,6 +14,8 @@ from semble.types import ContentType from semble.utils import is_git_url, resolve_model_name +logger = logging.getLogger(__name__) + if TYPE_CHECKING: from semble.index import SembleIndex @@ -48,11 +51,24 @@ def _linux_cache_dir(name: str) -> Path: return base / name +def _get_valid_user_cache_dir() -> Path | None: + """Gets the user cache dir if it is set and is a valid path.""" + user_cache_location = os.getenv("SEMBLE_CACHE_LOCATION") + if user_cache_location is None: + return None + user_cache_dir = Path(user_cache_location) + if not user_cache_dir.is_absolute(): + logger.warning("SEMBLE_CACHE_LOCATION is not an absolute path: %s", user_cache_location) + return None + + return user_cache_dir + + def resolve_cache_folder() -> Path: """Resolves a cache folder, respects SEMBLE_CACHE_LOCATION (highest precedence), XDG_CACHE_HOME.""" name = "semble" - if semble_cache_location := os.getenv("SEMBLE_CACHE_LOCATION"): - cache_dir = Path(semble_cache_location) + if user_cache_dir := _get_valid_user_cache_dir(): + cache_dir = user_cache_dir elif sys.platform == "win32": cache_dir = _windows_cache_dir(name) elif sys.platform == "darwin": diff --git a/tests/test_cache.py b/tests/test_cache.py index 995705d..1f4c934 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -8,6 +8,7 @@ import pytest from semble.cache import ( + _get_valid_user_cache_dir, _linux_cache_dir, _windows_cache_dir, clear_cache, @@ -93,6 +94,12 @@ def test_resolve_cache_folder(platform: str, mock_target: str, expected: Path) - assert result == expected +def test_get_valid_user_cache_dir_relative_path() -> None: + """_get_valid_user_cache_dir returns None when SEMBLE_CACHE_LOCATION is a relative path.""" + with patch.dict("os.environ", {"SEMBLE_CACHE_LOCATION": "relative/path"}): + assert _get_valid_user_cache_dir() is None + + def test_resolve_cache_folder_semble_cache_location(tmp_path: Path) -> None: """SEMBLE_CACHE_LOCATION takes precedence over all platform-specific helpers.""" custom = tmp_path / "custom_cache" From dfc1e49e198007154654b0e457ffd470ed8cae98 Mon Sep 17 00:00:00 2001 From: stephantul Date: Thu, 4 Jun 2026 13:07:05 +0200 Subject: [PATCH 2/2] fix test --- tests/test_cache.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_cache.py b/tests/test_cache.py index 1f4c934..d487564 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -97,7 +97,9 @@ def test_resolve_cache_folder(platform: str, mock_target: str, expected: Path) - def test_get_valid_user_cache_dir_relative_path() -> None: """_get_valid_user_cache_dir returns None when SEMBLE_CACHE_LOCATION is a relative path.""" with patch.dict("os.environ", {"SEMBLE_CACHE_LOCATION": "relative/path"}): - assert _get_valid_user_cache_dir() is None + with patch("semble.cache.logger") as mock_logger: + assert _get_valid_user_cache_dir() is None + mock_logger.warning.assert_called_once() def test_resolve_cache_folder_semble_cache_location(tmp_path: Path) -> None: