Skip to content

fix(file-safety): refuse writes into git-managed state under .git directories - #78806

Open
andrexibiza wants to merge 3 commits into
NousResearch:mainfrom
andrexibiza:fix/git-state-write-guard
Open

fix(file-safety): refuse writes into git-managed state under .git directories#78806
andrexibiza wants to merge 3 commits into
NousResearch:mainfrom
andrexibiza:fix/git-state-write-guard

Conversation

@andrexibiza

@andrexibiza andrexibiza commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Related #76246 #78565 #78652 #78653 #78793

Summary

write_file / patch / delete / move silently rewrite git-managed state inside a
normal repository's .git directory (HEAD, index, refs/, objects/, logs/,
packed-refs, ...). Reproduced on current main:

  • .git/HEAD → repo jumps to a phantom branch (On branch nonexistent), git log fatal
  • .git/refs/heads/xgit fsck: invalid sha1 pointer deadbeef…
  • .git/indexgit status/git fsck fatal: index file smaller than expected

The #78565 guard (worktree .git pointer FILES, PR #78652) does not cover this — a
.git component that is a DIRECTORY passes it. This PR closes the class: git-managed
state under a .git directory is refused; user-owned paths stay writable.

Fix

agent/file_safety.py:

  • find_git_managed_state_target(resolved_path) — walks every .git component; for
    DIRECTORY components, classifies the remainder: refused unless user-owned
    (allowlist: config, hooks/*, info/exclude, description); empty remainder
    (the .git dir itself) refused; .git FILE components are left to the existing
    pointer check.
  • New denial kind git_managed_state wired into _classify_write_denial and
    get_write_denied_error with a corruption-explaining message (use terminal git
    instead).
  • Same wiring in tools/file_tools.py:_check_sensitive_path (the write_file_tool /
    V4A wrapper surface).

Allowlist verified against git's repository-layout semantics: git itself never
rewrites config, hooks/*, info/exclude, description during normal operations —
those are user-owned; everything else under .git is git state.

Class audit (verified at this SHA): every mutating file surface is chokepointed through
get_write_denied_error (write_file :1457, patch_replace :1675, _python_delete
:1351, move_file :1397) or _check_sensitive_path (write_file_tool, patch_tool V4A
incl. both Move endpoints); V4A ops route through guarded methods; the ACP shim passes
the same guard. A rule in both functions = zero bypass through tools. Reads stay
permissive; terminal unaffected. Bare repositories (no .git component) remain out of
scope, noted for a separate hardening.

Note on the branch base

The #78565 guard family is not yet on main (open PR #78652). This branch cherry-picks
its two commits (6499b55335, 90483f2fe3) as the foundation — the git-state tests
depend on the pointer guard existing. When #78652 merges, those hunks dedupe; the new
material here is the git-managed-state guard (HEAD of the branch).

Tests

  • tests/tools/test_write_deny.py — refuse: .git/HEAD, .git/index,
    .git/refs/heads/x, .git/objects/ab/cdef, .git/logs/HEAD, .git/packed-refs,
    .git/ORIG_HEAD, .git/COMMIT_EDITMSG, .git/info/refs, .git itself; allow:
    .git/config, .git/hooks/pre-commit, .git/hooks/applypatch-msg,
    .git/info/exclude, .git/description, sibling files outside .git, and the
    worktree-pointer cases.
  • tests/tools/test_file_write_safety.py_check_sensitive_path surface, same
    allow/refuse split.
  • Targeted run on this host: 31 passed, 0 failed (pointer-guard classes +
    git-state classes).

Links

Part of #78565

…files

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
….git pointer guard

The shared deny-layer guard (file_safety) covers FileOperations verbs; the
write_file_tool entry point and the V4A patch wrapper guard via
_check_sensitive_path. Wire the same worktree .git pointer check there so
every mutating file surface refuses pointer-file targets, and add
wrapper-level regression tests.

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
…ectories

Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data tool/file File tools (read, write, patch, search) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: write_file/patch silently corrupt git-managed state in normal repos (.git/HEAD, refs/, index)

2 participants