From 02d1da49de5086946256cc157ff928dcffbe8ca1 Mon Sep 17 00:00:00 2001 From: LeonSGP43 Date: Sun, 31 May 2026 10:17:54 +0800 Subject: [PATCH] Block Hermes root config in media delivery --- gateway/platforms/base.py | 18 +++++++------ tests/gateway/test_platform_base.py | 39 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/gateway/platforms/base.py b/gateway/platforms/base.py index 26a6274ee..baa25b602 100644 --- a/gateway/platforms/base.py +++ b/gateway/platforms/base.py @@ -484,7 +484,7 @@ sys.path.insert(0, str(_Path(__file__).resolve().parents[2])) from gateway.config import Platform, PlatformConfig from gateway.session import SessionSource, build_session_key -from hermes_constants import get_hermes_dir, get_hermes_home +from hermes_constants import get_default_hermes_root, get_hermes_dir, get_hermes_home GATEWAY_SECRET_CAPTURE_UNSUPPORTED_MESSAGE = ( @@ -827,6 +827,7 @@ def cache_video_from_bytes(data: bytes, ext: str = ".mp4") -> str: DOCUMENT_CACHE_DIR = get_hermes_dir("cache/documents", "document_cache") SCREENSHOT_CACHE_DIR = get_hermes_dir("cache/screenshots", "browser_screenshots") _HERMES_HOME = get_hermes_home() +_HERMES_ROOT = get_default_hermes_root() MEDIA_DELIVERY_ALLOW_DIRS_ENV = "HERMES_MEDIA_ALLOW_DIRS" MEDIA_DELIVERY_TRUST_RECENT_ENV = "HERMES_MEDIA_TRUST_RECENT_FILES" MEDIA_DELIVERY_TRUST_RECENT_SECONDS_ENV = "HERMES_MEDIA_TRUST_RECENT_SECONDS" @@ -954,13 +955,14 @@ def _media_delivery_denied_paths() -> List[Path]: home = Path(os.path.expanduser("~")) for sub in _MEDIA_DELIVERY_DENIED_HOME_SUBPATHS: denied.append(home / sub) - # The Hermes home itself contains credentials (auth.json, .env) and - # configuration (config.yaml) — only the cache subdirectories under it - # are explicitly allowlisted above. - denied.append(_HERMES_HOME / ".env") - denied.append(_HERMES_HOME / "auth.json") - denied.append(_HERMES_HOME / "credentials") - denied.append(_HERMES_HOME / "config.yaml") + # The active Hermes profile and shared Hermes root both contain control + # files and credentials. Only cache subdirectories under them are + # explicitly allowlisted above. + for hermes_root in (_HERMES_HOME, _HERMES_ROOT): + denied.append(hermes_root / ".env") + denied.append(hermes_root / "auth.json") + denied.append(hermes_root / "credentials") + denied.append(hermes_root / "config.yaml") return denied diff --git a/tests/gateway/test_platform_base.py b/tests/gateway/test_platform_base.py index 2cc8118b7..c38070317 100644 --- a/tests/gateway/test_platform_base.py +++ b/tests/gateway/test_platform_base.py @@ -728,6 +728,45 @@ class TestMediaDeliveryDefaultMode: assert BasePlatformAdapter.validate_media_delivery_path(str(env_file)) is None + def test_denylist_blocks_hermes_config_in_active_profile(self, tmp_path, monkeypatch): + """The active profile config stays blocked in default mode.""" + self._patch_roots(monkeypatch) + + fake_home = tmp_path / "home" + hermes_dir = fake_home / ".hermes" + hermes_dir.mkdir(parents=True) + config_file = hermes_dir / "config.yaml" + config_file.write_text("model:\n provider: openai\n") + monkeypatch.setenv("HOME", str(fake_home)) + monkeypatch.setattr( + "gateway.platforms.base._HERMES_HOME", + hermes_dir, + ) + + assert BasePlatformAdapter.validate_media_delivery_path(str(config_file)) is None + + def test_denylist_blocks_shared_hermes_root_config_for_profiles(self, tmp_path, monkeypatch): + """Profile-mode gateways must still block the shared Hermes root config.""" + self._patch_roots(monkeypatch) + + fake_home = tmp_path / "home" + profile_home = fake_home / ".hermes" / "profiles" / "work" + profile_home.mkdir(parents=True) + hermes_root = fake_home / ".hermes" + config_file = hermes_root / "config.yaml" + config_file.write_text("profiles:\n active: work\n") + monkeypatch.setenv("HOME", str(fake_home)) + monkeypatch.setattr( + "gateway.platforms.base._HERMES_HOME", + profile_home, + ) + monkeypatch.setattr( + "gateway.platforms.base._HERMES_ROOT", + hermes_root, + ) + + assert BasePlatformAdapter.validate_media_delivery_path(str(config_file)) is None + def test_strict_mode_envvar_restores_legacy_behavior(self, tmp_path, monkeypatch): """Setting HERMES_MEDIA_DELIVERY_STRICT=1 reactivates the older allowlist+recency logic. A stale file outside the allowlist is