Skip to content

fix(security): eliminate info/exclude write (closes #16)#22

Merged
axisrow merged 1 commit into
mainfrom
sec-gitdir-eliminate
Jul 20, 2026
Merged

fix(security): eliminate info/exclude write (closes #16)#22
axisrow merged 1 commit into
mainfrom
sec-gitdir-eliminate

Conversation

@axisrow

@axisrow axisrow commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Closes #16. Third eliminate-redesign in the worktree security series (after leave-branch #12 and core.symlinks=false #20).

The original createWorktree wrote .worktrees/ into /info/exclude. That write was the entire #16 vuln: if gitDir resolved outside repoRoot (separate-git-dir, gitfile, linked worktree), the write landed at attacker location. Detection-based boundary checks broke legit linked worktrees and couldn't distinguish them from attack. Eliminate the write entirely: git worktree add works without it. Use in-repo .worktrees/.gitignore instead. 18/18 tests, 161 pass / 4 pre-existing.

….gitignore (closes #16)

Redesign (3rd eliminate in the worktree security series, after leave-branch
and core.symlinks=false). The original createWorktree wrote .worktrees/ into
<gitDir>/info/exclude to keep the worktree dir out of git status. That write
was the entire #16 vulnerability: if gitDir resolved outside repoRoot
(separate-git-dir, crafted .git gitfile, linked worktree), the exclude write
landed at an attacker-controlled location.

Detection-based fixes (boundary checks) broke after 2 iterations: git
introspection cannot authenticate external common-dirs — legit linked worktrees
gitdir in <main>/.git/worktrees/ is indistinguishable from attack separate-git-dir
via path inspection. Two independent Codex runs converged on the same conclusion:
eliminate the write, don't detect.

Fix: delete the entire git-dir/info/exclude block. git worktree add works WITHOUT
it (the exclude was cosmetic, from openai#137 commit 4bca062). Keep .worktrees/
out of git status via an in-repo .worktrees/.gitignore containing *, written
inside the already-validated worktreesDir (#14 guard ensures inside repoRoot,
not a symlink). No git-dir metadata write = no write outside repoRoot = #16 dead.

Regression tests: separate-git-dir repo now works (previously attack case);
linked worktree works (previously broken by boundary check); external
info/exclude untouched.

Verified: worktree 18/18; full npm test 161 pass / 4 pre-existing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@axisrow
axisrow force-pushed the sec-gitdir-eliminate branch from 3eb9480 to d791eab Compare July 20, 2026 08:06
@axisrow

axisrow commented Jul 20, 2026

Copy link
Copy Markdown
Owner Author

Cycle 1 triage: Codex HIGH (dangling .gitignore symlink redirect), Claude APPROVE (false positive — .worktrees already attacker-writable). Applied the lstat guard on .gitignore (defense-in-depth, 2 lines) in d791eab — closes both perspectives. 18/18 tests. Ready to merge.

@axisrow
axisrow merged commit 4063e6f into main Jul 20, 2026
@axisrow
axisrow deleted the sec-gitdir-eliminate branch July 20, 2026 08:08
axisrow added a commit that referenced this pull request Jul 20, 2026
Security hardening release: leave-branch worktree cleanup (#12) + 4 security
fixes (#18 symlink guard, #20 core.symlinks=false neutralize, #22 eliminate
info/exclude write, #23 accumulation warn). rescue default --background (#8).
test-teardown (#5). All verified with cycle-review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security(medium): git rev-parse --git-dir outside repoRoot → info/exclude append at attacker-chosen path

1 participant