Files
command-center/agent/managed-skills/grep-secret-gate-hook-traps/SKILL.md
T
Frank 8af40c402b chore(agent): commit the machine tier and the doctrine text the checkout was already running
Landing blocker for #61 and #63: the deployment checkout is 12 commits behind and
`merge --ff-only` refuses while tracked paths are dirty. Discard is not available here
(workspace policy: no `checkout`/`stash` on shared state), so the dirt is committed.

- `agent/APPEND_SYSTEM.md`: the point-of-use / one-work-log / PR-link doctrine, already
  byte-identical to `origin/main` (`cmp` against the trunk blob: no difference). No new
  prose — the commit exists so the trunk merge can proceed.
- `agent/config.yml` and `agent/managed-skills/**`: the machine-live tier the heartbeat
  owns (AGENTS.md § Live-sync tiers).

`ALLOW_SECRETS=1` was used, deliberately and after auditing the staged diff by hand: the
only credential-shaped strings in it are two copies of `AKIA0000000000000EXAMPLE` — the
inert all-zero AWS example id — and they appear inside the two skill pages that teach
that exact placeholder shape. This checkout is still running the pre-#61 hook; the merged
gate (value-scoped placeholder filter, `71601f7`) exempts this value, so the refusal
dies with this landing. No `--no-verify` was used: the gate ran and its verdict is
recorded here.

Refs #61
2026-10-21 01:44:51 -07:00

2.8 KiB

name, description
name description
grep-secret-gate-hook-traps Write a zero-dependency grep-based pre-commit secret gate that fails closed: the GNU grep -- one-shot trap, POSIX ERE limits, bracket collating elements, and rc=2-as-success in command substitution.

Fail-closed grep secret gate (pre-commit hook)

Use when writing/auditing a bash pre-commit hook (or any CI tripwire) that scans staged content with grep -E for credential shapes — no gitleaks available.

The four traps (all personally earned; each silently PASSES bad content)

  1. -- is a ONE-SHOT end-of-options marker. grep -nE -- 'p1' -- 'p2' 'p3' takes the later -- and patterns as FILE names → rc=2, errors on stderr. Correct multi-pattern form: repeat -e for every pattern; never --.
  2. if hits=$(... | grep ...) treats rc=2 as success. Any non-zero-but-not-1 (broken pattern, unreadable file) enters the then branch with EMPTY output. Never branch on the substitution's truthiness. Capture rc explicitly: hits=$(...); rc=$? then: rc>=2 → BLOCK (fail closed — a dead regex is worse than none), rc==1 → clean, rc==0 → hits.
  3. POSIX ERE (grep -E) has no (?:...) groups. Use plain (...).
  4. [:-] inside a bracket expression starts a collating element → grep: repetition-operator operand invalid (rc=2, see trap 2). Put - FIRST in every class: [-A-Za-z0-9/+=_.:+].

Gate structure that works

  • Scan staged content, not the working tree: git show ":$f" over git diff --cached --name-only --diff-filter=ACM.
  • Denylist doc/fixture paths (README, docs?/, tests?/, *.example) — else the hook blocks its own patterns and every test fixture.
  • Report BLOCKED <file>:<line> only — never echo the matched secret.
  • Wire with tracked hooks/pre-commit + git config core.hooksPath hooks (lives in the COMMON git dir config → one set in the main checkout covers every worktree). Fresh clones are unprotected until it's set; document it.
  • Worktrees lacking hooks/ while core.hooksPath is set: git dies on commit (cannot run ... No such file or directory) — merge main in, don't unset.
  • Do NOT ship the hook in an npm plugin artifact (files): node_modules never runs git; pin its ABSENCE in the pack test instead.

Test it red-first through the real seam (bats)

git init a fixture repo, cp the real hook in, git config core.hooksPath hooks, then drive REAL git commit and assert:

  • credential-shaped .env (RFC-2606 domains / all-zero ids as inert fixtures) → non-zero AND git log count unchanged (blocks, not warns);
  • PEM header line → blocked;
  • clean prose → passes;
  • the hook committing ITSELF → passes (trap 1/3/4 usually die here);
  • exit-code masking: bats ... | tail returns tail's rc — never && commit behind a piped suite, or you commit on red.