diff --git a/tests/tools/test_write_deny.py b/tests/tools/test_write_deny.py new file mode 100644 index 000000000..a525c3527 --- /dev/null +++ b/tests/tools/test_write_deny.py @@ -0,0 +1,83 @@ +"""Tests for _is_write_denied() — verifies deny list blocks sensitive paths on all platforms.""" + +import os +import pytest +from pathlib import Path + +from tools.file_operations import _is_write_denied + + +class TestWriteDenyExactPaths: + def test_etc_shadow(self): + assert _is_write_denied("/etc/shadow") is True + + def test_etc_passwd(self): + assert _is_write_denied("/etc/passwd") is True + + def test_etc_sudoers(self): + assert _is_write_denied("/etc/sudoers") is True + + def test_ssh_authorized_keys(self): + assert _is_write_denied("~/.ssh/authorized_keys") is True + + def test_ssh_id_rsa(self): + path = os.path.join(str(Path.home()), ".ssh", "id_rsa") + assert _is_write_denied(path) is True + + def test_ssh_id_ed25519(self): + path = os.path.join(str(Path.home()), ".ssh", "id_ed25519") + assert _is_write_denied(path) is True + + def test_netrc(self): + path = os.path.join(str(Path.home()), ".netrc") + assert _is_write_denied(path) is True + + def test_hermes_env(self): + path = os.path.join(str(Path.home()), ".hermes", ".env") + assert _is_write_denied(path) is True + + def test_shell_profiles(self): + home = str(Path.home()) + for name in [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile"]: + assert _is_write_denied(os.path.join(home, name)) is True, f"{name} should be denied" + + def test_package_manager_configs(self): + home = str(Path.home()) + for name in [".npmrc", ".pypirc", ".pgpass"]: + assert _is_write_denied(os.path.join(home, name)) is True, f"{name} should be denied" + + +class TestWriteDenyPrefixes: + def test_ssh_prefix(self): + path = os.path.join(str(Path.home()), ".ssh", "some_key") + assert _is_write_denied(path) is True + + def test_aws_prefix(self): + path = os.path.join(str(Path.home()), ".aws", "credentials") + assert _is_write_denied(path) is True + + def test_gnupg_prefix(self): + path = os.path.join(str(Path.home()), ".gnupg", "secring.gpg") + assert _is_write_denied(path) is True + + def test_kube_prefix(self): + path = os.path.join(str(Path.home()), ".kube", "config") + assert _is_write_denied(path) is True + + def test_sudoers_d_prefix(self): + assert _is_write_denied("/etc/sudoers.d/custom") is True + + def test_systemd_prefix(self): + assert _is_write_denied("/etc/systemd/system/evil.service") is True + + +class TestWriteAllowed: + def test_tmp_file(self): + assert _is_write_denied("/tmp/safe_file.txt") is False + + def test_project_file(self): + assert _is_write_denied("/home/user/project/main.py") is False + + def test_hermes_config_not_env(self): + path = os.path.join(str(Path.home()), ".hermes", "config.yaml") + assert _is_write_denied(path) is False diff --git a/tools/file_operations.py b/tools/file_operations.py index 0cf110531..8505444f0 100644 --- a/tools/file_operations.py +++ b/tools/file_operations.py @@ -42,32 +42,36 @@ from pathlib import Path _HOME = str(Path.home()) WRITE_DENIED_PATHS = { - os.path.join(_HOME, ".ssh", "authorized_keys"), - os.path.join(_HOME, ".ssh", "id_rsa"), - os.path.join(_HOME, ".ssh", "id_ed25519"), - os.path.join(_HOME, ".ssh", "config"), - os.path.join(_HOME, ".hermes", ".env"), - os.path.join(_HOME, ".bashrc"), - os.path.join(_HOME, ".zshrc"), - os.path.join(_HOME, ".profile"), - os.path.join(_HOME, ".bash_profile"), - os.path.join(_HOME, ".zprofile"), - os.path.join(_HOME, ".netrc"), - os.path.join(_HOME, ".pgpass"), - os.path.join(_HOME, ".npmrc"), - os.path.join(_HOME, ".pypirc"), - "/etc/sudoers", - "/etc/passwd", - "/etc/shadow", + os.path.realpath(p) for p in [ + os.path.join(_HOME, ".ssh", "authorized_keys"), + os.path.join(_HOME, ".ssh", "id_rsa"), + os.path.join(_HOME, ".ssh", "id_ed25519"), + os.path.join(_HOME, ".ssh", "config"), + os.path.join(_HOME, ".hermes", ".env"), + os.path.join(_HOME, ".bashrc"), + os.path.join(_HOME, ".zshrc"), + os.path.join(_HOME, ".profile"), + os.path.join(_HOME, ".bash_profile"), + os.path.join(_HOME, ".zprofile"), + os.path.join(_HOME, ".netrc"), + os.path.join(_HOME, ".pgpass"), + os.path.join(_HOME, ".npmrc"), + os.path.join(_HOME, ".pypirc"), + "/etc/sudoers", + "/etc/passwd", + "/etc/shadow", + ] } WRITE_DENIED_PREFIXES = [ - os.path.join(_HOME, ".ssh") + os.sep, - os.path.join(_HOME, ".aws") + os.sep, - os.path.join(_HOME, ".gnupg") + os.sep, - os.path.join(_HOME, ".kube") + os.sep, - "/etc/sudoers.d" + os.sep, - "/etc/systemd" + os.sep, + os.path.realpath(p) + os.sep for p in [ + os.path.join(_HOME, ".ssh"), + os.path.join(_HOME, ".aws"), + os.path.join(_HOME, ".gnupg"), + os.path.join(_HOME, ".kube"), + "/etc/sudoers.d", + "/etc/systemd", + ] ]