Identity hook: a push with no refspec is read from git's configuration, and a git the command may redefine is refused
Closes #111.
The identity hook refuses a commit, a push or a GitHub write that isn't the Maintainer's. Until now it read a push with no refspec as HEAD, and it trusted that git in the command was git. Now:
A push with no refspec, or with no remote, is read from git's configuration. One reader in identity.py, which both hooks call:
- What it reads: the current branch's
pushRemote, thenremote.pushDefault, then the branch's own remote; andpush.default,remote.*.pushandremote.*.mirror. - How it reads them:
mirroris read as--mirror.- Configured refspecs are read as if given on the command line.
matchingis read as--branches.- Every other
push.defaultvalue is read as HEAD.
- What it refuses: a wildcard, an unknown value, or config it cannot read.
- A push to
.writes nothing to GitHub and is not read. - How config is read: git's configuration files are read directly, with no
--dry-run, because that reaches the network and the credential helper.
A push setting given by -c in the command is refused, not read. There is one shared PUSH_KEYS list, which adds remote.pushDefault and remote.*.mirror.
A git the command may redefine is refused when the command commits or pushes:
- a function or alias named
git,gh,command,builtinor an allowed wrapper (zsh'sfunctions[]/aliases[],hashandenablecount); PATHset;execorbuiltin;- an unlisted wrapper,
sudoamong them; - a program in a variable the hook cannot read, given a write verb.
git counts only in command position, past the wrappers the shared reader resolves (tools/hooks/shell.py, reused from #142). So gitleaks git <path> is no longer refused as an unknown git command; it was refused on main before this.
Working rulings taken without the Maintainer, open to review: a PATH set in a commit or push command is refused, and so is a commit or push through an unlisted wrapper such as sudo.
Decision 0024 adds this to 0017. 0017's Status is unchanged, since check_decisions allows a Status to change only to "replaced by".
Verified on a plain git archive export with PATH=/usr/bin:/bin, at aa68d54:
make verifypasses (516 tests).python3 -m tools.mutantskills 436 of 436, and each identity and shell mutant fails its own test.check_decisionsis clean.
Reviewed by: a fresh-context Claude review and a security pass. All three false refusals they found are fixed:
gitleaks git <path>;- every bare
git pushrefused because some other branch had a local upstream; env -- git push.
AGY (Gemini 4 Argon) on f95da81, and CodeRabbit: ten findings, all real, fixed in 46116b5 and aa68d54. This was the one fix round for spellings.
- False refusal: commit-message or PR-body prose mentioning PATH was refused. Only shell words are read now.
- A missed wrapper argument:
timeout -- 60 claude …read "60" as the program. - Variables: identity variables and PATH passed through a variable to
env. whichredefined:which,typeandwhencecount as redefinitions, and only a plain name inside$(which …)is rewritten.- Wrappers by path: a wrapper given by a path, such as
./nice, refuses. - Function names: quoted or escaped names, and zsh's several names after
function, are caught. - Empty config: an empty
push.defaultasks. - Reserved words: the shell-source check reads past
time,ifand!. - Record wording: 0024 no longer recounts review history.
Five spellings that no session would write by mistake are filed as #148. The Stop check and the rulesets are the boundary (#23's ruling).
Also: a pre-check keeps the protected hook inside its 5-second budget on a 200 KB command under load.
🤖 Generated with Claude Code
Summary by CodeRabbit
- Bug Fixes
- Push checks now account for configured remotes, default destinations, refspecs, and mirror settings when no destination is specified.
- The identity check refuses pushes when relevant Git settings cannot be read or resolved, and flags commands that may conceal or redefine Git or GitHub CLI.
- Pushes to local repositories are handled without GitHub-account or commit checks. Protected-branch checks handle ambiguous push destinations conservatively.
- Identity checks now catch additional ways commands can change Git identity or configuration, including through expanded environment values.
- Documentation
- Updated guidance on hook command parsing and Git push configuration.