Identity hook: a write into git's directory, an alias that runs git, and xargs git are refused
Merged
DO
2d ago+684+684+012345678901234567890123456789-19-19-01234567890123456789
Closes #148.
These spellings were left after the push-reading story (#111, PR #149). The rulings are on #148, and decision 0030 records them.
What changes
- A write into git's directory in a command that commits or pushes is refused (
git_directory_written).- What counts as a write: only a redirect's target, or the destination of a known writer: cp, mv, ln, install, tee, rsync, tar, dd, and
sed/perl -i. - Paths: resolved through
cd,.., links and case. A loop variable is read as each word of its list. A glob or brace that can expand to.gitcounts;*alone doesn't, as in the shell. - Not writes:
find -not -path '*/.git/*',fd -E .git,tree -I .git,rsync --exclude .git,sed -n/awk/stat/dureading.git, and git's own words such asgit commit -F .git/MERGE_MSG.
- What counts as a write: only a redirect's target, or the destination of a known writer: cp, mv, ln, install, tee, rsync, tar, dd, and
- An alias whose value runs git or gh is refused, with quotes removed, both as
aliasand as zsh'saliases[g]. Soalias g=gitis refused on its own. An alias value the hook cannot read is refused too. xargsorparallelgiving git no verb isgit_command_unknown.xargs git addpasses.- Loops and
case: afor/selectword list and everycasearm's pattern are data. This fixes a false refusal of a read-only loop over file names that contain "git" and "commit". - A gh alias is left to the Stop check and the rulesets: it doesn't change which account writes. That is a working ruling.
Verified:
make verifypasses (531 tests) on a plaingit archiveexport withPATH=/usr/bin:/bin. The record was renumbered there only, for the gap.- Each of the 25 mutants was killed by its own test, checked one at a time by hand. CI's shards run the set.
check_decisionsis clean.
Reviewed by: a fresh-context Claude review, a security pass, and AGY (Gemini 4 Argon) on 698564a. Everything is fixed in 6de5f42; this was the story's one fix round for spellings.
- False refusals fixed:
find/fd/treeexcludes,git commit -F .git/MERGE_MSG, readers of.git, andcasearms after the first line. - Security gaps closed: a quote-split alias, a
forloop binding a.gitpath, a glob into.git, and two branches with no mutant. - AGY's zsh table-alias case was already refused, and is now tested.
Left, to be filed:
cp x .git/hooks/pre-pushon its own is still refused, as onmain, because "pre-push" in the path matches.- An unlisted writer such as
python3 -cwriting into.gitpasses.
Order: merges after #16 (decision 0029).
🤖 Generated with Claude Code
Summary by CodeRabbit
- Safety
- Improved safeguards against commands that could alter Git metadata before commit or push operations.
- Added checks for Git and GitHub CLI commands invoked through aliases or input-fed commands.
- Shell loops and
casestatements are now interpreted more accurately, distinguishing commands that run from patterns and other data.
- Documentation
- Added guidance describing these command-handling rules and their boundaries.