Skip to content

Detect secrets

detect-secrets runs on every PreToolUse for git commit and fails the call if any staged file contains a recognisable credential pattern. It’s the second line of defence after the .env.octopus / .gitignore convention — the line that catches the moment when a fresh credential lands inline in a config or a script.

Why this exists

Hardcoded secrets are the dominant cause of credentials leaking to public repositories. The signal-to-noise ratio is high: real secrets follow narrow grammars (sk_live_… for Stripe, xoxb-… for Slack, AKIA… for AWS access keys, ghp_… for GitHub tokens, JWT three-segment base64), so regex detection catches the vast majority of accidental commits with very few false positives.

The hook runs before the commit is created, so a blocked secret never enters git history — even local. That matters: once a secret hits a commit object, even amending and force-pushing doesn’t always erase it from forks, mirrors, and caches.

Patterns detected

The current ruleset covers the providers that show up in this codebase’s .env.example files plus a few high-blast-radius ones added defensively:

  • AWS access key IDs (AKIA, ASIA prefixes)
  • Stripe live and test keys (sk_live_, sk_test_, pk_live_)
  • SendGrid (SG.…)
  • Slack bot and user tokens (xoxb-, xoxp-, xoxa-)
  • GitHub personal access tokens (ghp_, gho_, ghu_, ghs_, ghr_)
  • OpenAI keys (sk-proj-…, sk-…)
  • Generic JWTs (three base64url segments separated by dots)
  • Private key blocks (-----BEGIN … PRIVATE KEY-----)

False positives

The patterns are tight, but the JWT and generic key patterns can match in two legitimate places:

  • Example values in docs and READMEs — placeholders that happen to look like the real shape (sk-test-xxxxxxxx…).
  • Inline tests for the secret detector itself.

For docs and tests, the convention is to prefix or suffix with example, placeholder, XXXX, or use deliberately invalid characters so the pattern doesn’t match. When a real false positive does occur, file it as a bug and tighten the regex — don’t add an inline ignore.

How to bypass

There is no bypass marker by design. If a value looks like a secret to the hook, it looks like one to a future reader too — fix the value (move to .env.octopus, rotate, or replace with a clearly-fake placeholder) rather than annotating around the warning.

Pairing with the audit-security skill

detect-secrets catches the moment of accidental introduction. The audit-security skill catches secrets that slipped through earlier — running a wider scan across the working tree, history, and .env* files. They’re complementary: one is a fast PreToolUse gate, the other is a deliberate audit pass before merge.

Source: hooks/pre-tool-use/detect-secrets.sh