mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(file_tools): block /private/etc writes on macOS symlink bypass
On macOS, /etc is a symlink to /private/etc, so os.path.realpath() resolves /etc/hosts to /private/etc/hosts. The sensitive path check only matched /etc/ prefixes against the resolved path, allowing writes to system files on macOS. - Add /private/etc/ and /private/var/ to _SENSITIVE_PATH_PREFIXES - Check both realpath-resolved and normpath-normalized paths - Add regression tests for macOS symlink bypass Closes #8734 Co-authored-by: ElhamDevelopmentStudio (PR #8829)
This commit is contained in:
parent
587eeb56b9
commit
311dac1971
2 changed files with 41 additions and 11 deletions
|
|
@ -79,5 +79,33 @@ class TestSafeWriteRoot:
|
|||
assert _is_write_denied(os.path.expanduser("~/.ssh/id_rsa")) is True
|
||||
|
||||
|
||||
class TestCheckSensitivePathMacOSBypass:
|
||||
"""Verify _check_sensitive_path blocks /private/etc paths (issue #8734)."""
|
||||
|
||||
def test_etc_hosts_blocked(self):
|
||||
from tools.file_tools import _check_sensitive_path
|
||||
assert _check_sensitive_path("/etc/hosts") is not None
|
||||
|
||||
def test_private_etc_hosts_blocked(self):
|
||||
from tools.file_tools import _check_sensitive_path
|
||||
assert _check_sensitive_path("/private/etc/hosts") is not None
|
||||
|
||||
def test_private_etc_ssh_config_blocked(self):
|
||||
from tools.file_tools import _check_sensitive_path
|
||||
assert _check_sensitive_path("/private/etc/ssh/sshd_config") is not None
|
||||
|
||||
def test_private_var_blocked(self):
|
||||
from tools.file_tools import _check_sensitive_path
|
||||
assert _check_sensitive_path("/private/var/db/something") is not None
|
||||
|
||||
def test_boot_still_blocked(self):
|
||||
from tools.file_tools import _check_sensitive_path
|
||||
assert _check_sensitive_path("/boot/grub/grub.cfg") is not None
|
||||
|
||||
def test_safe_path_allowed(self):
|
||||
from tools.file_tools import _check_sensitive_path
|
||||
assert _check_sensitive_path("/tmp/safe_file.txt") is None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
|
|
|
|||
|
|
@ -92,7 +92,10 @@ def _is_blocked_device(filepath: str) -> bool:
|
|||
|
||||
# Paths that file tools should refuse to write to without going through the
|
||||
# terminal tool's approval system. These match prefixes after os.path.realpath.
|
||||
_SENSITIVE_PATH_PREFIXES = ("/etc/", "/boot/", "/usr/lib/systemd/")
|
||||
_SENSITIVE_PATH_PREFIXES = (
|
||||
"/etc/", "/boot/", "/usr/lib/systemd/",
|
||||
"/private/etc/", "/private/var/",
|
||||
)
|
||||
_SENSITIVE_EXACT_PATHS = {"/var/run/docker.sock", "/run/docker.sock"}
|
||||
|
||||
|
||||
|
|
@ -102,17 +105,16 @@ def _check_sensitive_path(filepath: str) -> str | None:
|
|||
resolved = os.path.realpath(os.path.expanduser(filepath))
|
||||
except (OSError, ValueError):
|
||||
resolved = filepath
|
||||
normalized = os.path.normpath(os.path.expanduser(filepath))
|
||||
_err = (
|
||||
f"Refusing to write to sensitive system path: {filepath}\n"
|
||||
"Use the terminal tool with sudo if you need to modify system files."
|
||||
)
|
||||
for prefix in _SENSITIVE_PATH_PREFIXES:
|
||||
if resolved.startswith(prefix):
|
||||
return (
|
||||
f"Refusing to write to sensitive system path: {filepath}\n"
|
||||
"Use the terminal tool with sudo if you need to modify system files."
|
||||
)
|
||||
if resolved in _SENSITIVE_EXACT_PATHS:
|
||||
return (
|
||||
f"Refusing to write to sensitive system path: {filepath}\n"
|
||||
"Use the terminal tool with sudo if you need to modify system files."
|
||||
)
|
||||
if resolved.startswith(prefix) or normalized.startswith(prefix):
|
||||
return _err
|
||||
if resolved in _SENSITIVE_EXACT_PATHS or normalized in _SENSITIVE_EXACT_PATHS:
|
||||
return _err
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue