58 lines
2.3 KiB
Python
58 lines
2.3 KiB
Python
"""Secret stores under HERMES_HOME are write-denied; control files stay writable (#110464).
|
|
|
|
``get_read_block_error`` refuses every credential store. The write side is deliberately
|
|
narrower — #45947 freed ``auth.json`` / ``config.yaml`` / ``webhook_subscriptions.json`` so
|
|
the user can ask to edit them — but the secret *material* it meant to keep blocked had
|
|
drifted: ``auth/google_oauth.json``, the plaintext Bitwarden cache, ``vault/`` and
|
|
``browser-profile/`` were writable through ``write_file`` / ``patch``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import agent.file_safety as fs
|
|
|
|
SECRET_STORES = (
|
|
"auth/google_oauth.json", "cache/bws_cache.json", "vault/vault.key", "browser-profile/Default/Cookies",
|
|
)
|
|
WRITABLE_CONTROL_FILES = ("auth.json", "config.yaml", "webhook_subscriptions.json")
|
|
|
|
|
|
@pytest.fixture()
|
|
def hermes_layout(tmp_path, monkeypatch):
|
|
"""Profile HERMES_HOME plus a distinct global root, both patched."""
|
|
root = tmp_path / "hermes_root"
|
|
profile = root / "profiles" / "coder"
|
|
profile.mkdir(parents=True)
|
|
monkeypatch.setattr(fs, "_hermes_home_path", lambda: profile)
|
|
monkeypatch.setattr(fs, "_hermes_root_path", lambda: root)
|
|
return root, profile
|
|
|
|
|
|
def _touch(base: Path, rel: str) -> Path:
|
|
p = base / rel
|
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
p.write_text("dummy", encoding="utf-8")
|
|
return p
|
|
|
|
|
|
def test_read_denied_secret_stores_are_write_denied_on_profile_and_root(hermes_layout):
|
|
root, profile = hermes_layout
|
|
for base in (profile, root):
|
|
for rel in SECRET_STORES:
|
|
path = _touch(base, rel)
|
|
assert fs.get_read_block_error(str(path)), f"fixture drift: not read-denied: {path}"
|
|
assert fs.is_write_denied(str(path)), f"write allowed: {path}"
|
|
|
|
|
|
def test_control_files_and_lookalikes_outside_home_stay_writable(hermes_layout, tmp_path):
|
|
root, profile = hermes_layout
|
|
for base in (profile, root):
|
|
for rel in WRITABLE_CONTROL_FILES:
|
|
assert fs.is_write_denied(str(_touch(base, rel))) is False, f"#45947 regression: {rel}"
|
|
assert fs.is_write_denied(str(_touch(tmp_path / "myproject", "cache/bws_cache.json"))) is False
|
|
assert fs.is_write_denied(str(_touch(tmp_path / "myproject", "vault/vault.key"))) is False
|