fix(security): re-resolve checkpoint/sticker-cache paths per call

tools/checkpoint_manager.py's CHECKPOINT_BASE and gateway/sticker_cache.py's
CACHE_PATH are resolved once at import time via get_hermes_home(), which is
a context-local ContextVar under the multiplexed gateway (multiple profiles
sharing one process). Freezing the path at import time pins every later
checkpoint/cache read-write to whichever profile's HERMES_HOME was active
when the module was first imported -- the same bug class already fixed for
cache dirs, skills_hub, rich_sent_store, and (this session) the OAuth/auth.json/
sessions.json paths.

CheckpointManager is "owned by AIAgent" per-instance, but its methods read
the frozen module constant directly instead of taking the store root from
the instance, so a profile's CheckpointManager can read/write code-edit
checkpoints into a different profile's store.

Add a per-call resolver for each path, following the established "respect
an existing test monkeypatch of the constant, otherwise re-resolve through
get_hermes_home()" pattern so the extensive existing test seams in
tests/tools/test_checkpoint_manager.py and tests/gateway/test_sticker_cache.py
keep working unmodified.

(cherry picked from commit 03ae075d969094cb584e6ab38d2a773d15ff875c)
(cherry picked from commit b850c4b18e2ae2158a97c6cb87bd2057918b8170)
This commit is contained in:
srojk34
2026-07-01 13:57:07 +03:00
committed by Teknium
parent 819517fbac
commit e70db09f51
3 changed files with 70 additions and 11 deletions
+41
View File
@@ -156,6 +156,47 @@ class TestGatewayHooksDirResolution:
assert "only-in-b" in b_hooks
class TestCheckpointManagerPathResolution:
"""tools/checkpoint_manager.py's checkpoint store root must honor the
active profile — otherwise one profile's CheckpointManager instance can
read/write code-edit checkpoints into a different profile's store under
the multiplexed gateway."""
def test_checkpoint_base_follows_override(self, two_profiles):
prof_a, prof_b = two_profiles
import tools.checkpoint_manager as cm
a_seen = _under_override(prof_a, lambda: cm._resolve_checkpoint_base())
b_seen = _under_override(prof_b, lambda: cm._resolve_checkpoint_base())
assert a_seen == prof_a / "checkpoints"
assert b_seen == prof_b / "checkpoints"
assert a_seen != b_seen
def test_store_path_follows_override(self, two_profiles):
prof_a, prof_b = two_profiles
import tools.checkpoint_manager as cm
b_seen = _under_override(prof_b, lambda: cm._store_path())
assert b_seen == prof_b / "checkpoints" / "store"
class TestStickerCachePathResolution:
"""gateway/sticker_cache.py's cache file must honor the active profile —
otherwise one profile's Telegram sticker-description cache leaks into a
different profile's under the multiplexed gateway."""
def test_cache_path_follows_override(self, two_profiles):
prof_a, prof_b = two_profiles
import gateway.sticker_cache as sc
a_seen = _under_override(prof_a, lambda: sc._resolve_cache_path())
b_seen = _under_override(prof_b, lambda: sc._resolve_cache_path())
assert a_seen == prof_a / "sticker_cache.json"
assert b_seen == prof_b / "sticker_cache.json"
assert a_seen != b_seen
# ---------------------------------------------------------------------------
# M2 — thread / executor context propagation