From e2d80b68856ed41ec08b2a34f6b4c75e53082a12 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 13 Jun 2026 06:52:11 -0700 Subject: [PATCH] feat(security): deny writes to startup and global git config Block write tool edits to secondary shell startup files and global git config paths while preserving project-local `.git/config` writes. --- agent/file_safety.py | 5 +++++ tests/tools/test_write_deny.py | 17 ++++++++++++++++- tools/skills_guard.py | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/agent/file_safety.py b/agent/file_safety.py index e9fa487e834f..709098e24fd2 100644 --- a/agent/file_safety.py +++ b/agent/file_safety.py @@ -51,6 +51,10 @@ def build_write_denied_paths(home: str) -> set[str]: os.path.join(home, ".profile"), os.path.join(home, ".bash_profile"), os.path.join(home, ".zprofile"), + os.path.join(home, ".zshenv"), + os.path.join(home, ".zlogin"), + os.path.join(home, ".bash_login"), + os.path.join(home, ".gitconfig"), os.path.join(home, ".netrc"), os.path.join(home, ".pgpass"), os.path.join(home, ".npmrc"), @@ -78,6 +82,7 @@ def build_write_denied_prefixes(home: str) -> list[str]: os.path.join(home, ".azure"), os.path.join(home, ".config", "gh"), os.path.join(home, ".config", "gcloud"), + os.path.join(home, ".config", "git"), ] ] diff --git a/tests/tools/test_write_deny.py b/tests/tools/test_write_deny.py index 6fe6c802aced..9462f6698336 100644 --- a/tests/tools/test_write_deny.py +++ b/tests/tools/test_write_deny.py @@ -69,9 +69,24 @@ class TestWriteDenyExactPaths: def test_shell_profiles(self): home = str(Path.home()) - for name in [".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile"]: + for name in [ + ".bashrc", ".zshrc", ".profile", ".bash_profile", ".zprofile", + ".zshenv", ".zlogin", ".bash_login", + ]: assert _is_write_denied(os.path.join(home, name)) is True, f"{name} should be denied" + def test_global_git_config_paths(self): + home = str(Path.home()) + for path in [ + os.path.join(home, ".gitconfig"), + os.path.join(home, ".config", "git", "config"), + os.path.join(home, ".config", "git", "hooks", "pre-commit"), + ]: + assert _is_write_denied(path) is True, f"{path} should be denied" + + def test_project_git_config_allowed(self): + assert _is_write_denied("/tmp/someproject/.git/config") is False + def test_package_manager_configs(self): home = str(Path.home()) for name in [".npmrc", ".pypirc", ".pgpass"]: diff --git a/tools/skills_guard.py b/tools/skills_guard.py index a1a8606d6d47..c64f0dfa9617 100644 --- a/tools/skills_guard.py +++ b/tools/skills_guard.py @@ -252,7 +252,7 @@ THREAT_PATTERNS = [ (r'\bcrontab\b', "persistence_cron", "medium", "persistence", "modifies cron jobs"), - (r'\.(bashrc|zshrc|profile|bash_profile|bash_login|zprofile|zlogin)\b', + (r'\.(bashrc|zshrc|zshenv|profile|bash_profile|bash_login|zprofile|zlogin)\b', "shell_rc_mod", "medium", "persistence", "references shell startup file"), (r'authorized_keys',