Secret scanning
Carbon ships a small, deterministic secret scanner. The same pattern list
that scrubs the runtime access log (SECRET_VALUE_PATTERNS in
@carbon/secret-scan) also runs against your working tree, at three
distinct points, so a credential has to slip past all three to reach a
public branch:
- Locally, on demand —
carbon audit-secretswalks the tree and fails with exit code1on any finding. - On every commit — the pre-commit hook installed at
.githooks/runs the scanner over the staged files.pnpm install’spreparescript pointscore.hooksPathat that directory automatically. - In CI — the
testjob runscarbon audit-secretsafter the unit suite. A finding fails the job, which blocksdeploy.
What it catches
Section titled “What it catches”| Prefix or shape | Kind label |
|---|---|
ck_live_… | carbon-key |
sk_live_… / sk_test_… | stripe-secret |
sk-… (OpenAI / Anthropic-style) | openai-anthropic |
xoxb- / xoxp- / xoxa-… | slack-token |
ghp_ / gho_ / ghu_ / ghs_ / ghr_… | github-token |
AKIA… / ASIA… (AWS access key id) | aws-access-key |
-----BEGIN … PRIVATE KEY----- | private-key-pem |
postgres://user:pw@host and friends | db-url-with-password |
Patterns are deliberately anchored on unambiguous prefixes. A naked base64/hex blob will not match — it produces too many false positives against opaque request-body ids to be useful in a hard-fail linter.
Running it manually
Section titled “Running it manually”# Scan the whole tree.carbon audit-secrets
# Scan a subset.carbon audit-secrets apps/api src/config
# Scripting: one JSON object per finding, plus a summary line.carbon audit-secrets --jsonExit code:
0— no findings.1— one or more findings. The offending kind, file, line, and a shape-preserving preview (never the full value) are printed.
Adding a pattern
Section titled “Adding a pattern”Edit packages/secret-scan/src/patterns.ts. Both the CLI and the API
runtime pick up the addition on the next build. Tests live alongside in
scan.test.ts; fixtures are assembled at runtime so the test file itself
never trips the scanner or GitHub push-protection.
Why we don’t also match secret access keys
Section titled “Why we don’t also match secret access keys”AWS secret access keys are 40 characters of base64. Every S3 object key
in a request body, every content hash, every opaque per-tenant id in a
recording collides with that shape. A hard-fail scanner that cries wolf
on those quickly gets disabled — a policy failure much worse than the
one it was preventing. The access-key id (AKIA… / ASIA…) is
diagnostic enough on its own: if it leaks, so did the paired secret.