Git hook audit .git/hooks/ persistence + injection audit (G1-G7).

Every developer host has dozens of git repos. .git/hooks/* runs with the user's privileges on every routine git operation — and a poisoned post-checkout runs silently on every git pull. That's a textbook silent-persistence primitive. The existing TrapDoor detector only matches specific campaign markers in hooks; this auditor catches the general patterns.

Strictly local

The auditor never runs a hook, never alters the repo, never calls out to the network. It walks the directory tree, finds every repo (.git/ child), honors core.hooksPath if overridden, parses each executable hook, and records:

What's skipped

Safety caps: 1 MiB per hook, 64 hooks per repo, 5000 repos per root.

Detection layers (G1–G7)

IDSeverityWhat it catchesMITRE
G1criticalPipe-to-shell download cradle: curl ... | sh. Essentially never legitimate in a hook.T1059
G2highNetwork-fetch in a silent-operation hook (post-checkout / post-merge / post-rewrite / pre-push / post-commit). Fires on every routine git operation — any C2 callback becomes every-time-the-dev-touches-git.T1546
G3mediumNetwork-fetch in a non-silent hook. Rarely needed; manual review.T1546
G4highEval of attacker-controllable input (eval "$VAR" / eval $1). In commit-msg the input is the commit message itself; in post-checkout it's the ref name.T1059
G5highSelf-modifying hook (writes to .git/config, .git/hooks/, .git/packed-refs). Hook persistence + self-replication primitive.T1546
G6mediumLong base64 or escaped-hex sequence in hook. Often hides a download cradle or shellcode.T1027
G7infoSilent-operation hook present (no other layer fired). Surface-area awareness so a tampered one stands out next time.T1546

A clean is_known_sample hook emits NO findings. The "stronger finding suppresses weaker" semantics apply: G1 suppresses G2/G3; any specific finding suppresses the G7 info finding.

CLI

$ digger git audit-hooks --case-dir /tmp/case [--roots ~/src,~/work]

[git] executable hooks audited: 47
[git]   pipe-to-shell:    0
[git]   network-fetch:    2
[git]   eval input:       0
[git]   self-modifying:   0
[git]   encoded payload:  1
[git]   silent-op hooks:  18
[git] artifacts emitted: 47