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:
konsisumer 2026-04-13 05:14:41 -07:00 committed by Teknium
parent 587eeb56b9
commit 311dac1971
2 changed files with 41 additions and 11 deletions

View file

@ -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