v0.3.5 — a credential leak in staged-secret-guard
staged-secret-guard could let a staged credential through. If you use git commit -u, update.
claude plugin update guard-hooks@cc-agents-kitThen restart open sessions. Affected releases are 0.3.2, 0.3.3, and 0.3.4; earlier ones do not carry the defect.
The leak
The guard's git commit flag table put -u / --untracked-files among the options that consume the next token as their value. git defines them as -u[<mode>] — the value is attached and optional, never a separate token.
So on git commit -u -m "fix: x" the guard read -m as -u's value and fix: x as a pathspec. It built a commit candidate for a path that does not exist, scanned nothing, and exited 0. Real git meanwhile parsed both flags correctly and committed the staged content. Confirmed end to end in a throwaway repository: exit 0, and the token sitting in HEAD.
-S / --gpg-sign carries the same optional-attached shape and is now handled with it. Both the separate-token forms and the attached forms (-uall, -S<key-id>) are covered by regression cases that exit 0 against the previous table.
The false block
git commit -q was refused as unparseable. -q / --quiet was simply absent from the no-value flag list, so it fell to the catch-all that blocks an unrecognised flag — a benign and extremely common flag failing closed. It is added along with the other no-value flags the table had omitted, each read off git commit's own option list rather than recalled: -z / --null, --verify, --no-signoff, --reset-author, --allow-empty, --allow-empty-message, --status / --no-status, --no-gpg-sign.
What stays refused, on purpose
- Flags that change which content is committed:
-i,-p,--interactive,--pathspec-from-file. -e/--edit. It parses fine, but it opens$EDITORagainst a shell with no TTY, so admitting it would trade a millisecond refusal for a hung tool call.- Bundled short flags (
-sq) and anything unrecognised. The table is an allowlist because an unknown flag might take a value, and mis-parsing one is exactly how the leak above happened. The README's known-limits section now says so, rather than implying the list is exhaustive.
Verification
Every new case distinguishes "parsed, scanned, found the credential" from "refused to parse" — both exit 2, so the exit code alone proves nothing.
Coverage was checked by mutation, with none skipped. Four mutations, four caught: restoring -u to the value-consuming list, dropping -q, dropping the attached-value arm, and widening the list far enough to admit -p.
Versions
guard-hooks 0.2.3. context-handoff 0.1.2 and repo-gate 0.1.4 are unchanged.