Audit the working tree and git history for exposed credentials and mis-scoped keys, then a rotate-vs-relocate plan. Use when "hardcoded secrets", "did I commit a key", "secret scan", "is my .env safe", or "rotate keys". Do not fire for "RLS audit" or generic "security burndown".
Scanned 9/11/2026
Install to Claude Code
npx -y skills add kensaurus/cursor-kenji --skill plan-secrets-audit --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Plan Secrets Audit?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/kensaurus-plan-secrets-audit)More formats (shields.io, HTML) on the badges page.
---
name: plan-secrets-audit
description: >
Audit the working tree and git history for exposed credentials and mis-scoped
keys, then a rotate-vs-relocate plan. Use when "hardcoded secrets", "did I
commit a key", "secret scan", "is my .env safe", or "rotate keys". Do not fire
for "RLS audit" or generic "security burndown".
license: MIT
---
# Secrets & Key-Scope Audit + Rotation Plan
**Degree of freedom: MIXED** — prefix scan is exact; rotate-vs-relocate
is judgment. Stay **plan-only**. Never print secret values.
## This skill vs neighbors
| Skill | Owns |
|---|---|
| **plan-secrets-audit** (this) | Rotate vs relocate leaked keys |
| `plan-security-audit` | OWASP umbrella (not the first secrets match) |
| `plan-rls-audit` | Table access control |
| `audit-env-parity` | Env/config drift |
## How to reason (every plan item)
1. **Propose** — rotate, relocate, or hygiene — say which, never print the value
2. **Risk** — who can use the key if it stays live (history counts)
3. **Keep-working** — keys that are scoped correctly and not in history
4. **Phase** — rotate → relocate → hygiene → optional scrub (do not execute)
## Worked example
> **Propose:** rotate the Stripe secret at `api/pay.ts` (last-4 only in the report); history presence means rotate, not `.env` move.
> **Risk:** `sk_` in committed history stays valid forever after a relocate.
> **Keep-working:** Stripe `pk_` in the client is publishable by design (still note the protector).
> **Phase:** Phase 1 — rotate exposed never-client secrets.
> **Redaction:** type + path + last 4 — never the secret.
**Role:** Senior security engineer (credential exposure + key scoping).
**Task:** Scan working tree and git history, classify each credential (rotate vs
relocate), score by scope and permanence, emit `plan-secrets-audit.md`. **Audit & plan
only — no rotation, scrubbing, or env edits until each phase is approved.**
**Find every leaked key. Decide rotate vs relocate. Change nothing until approved.**
A widely reported breach started with a hardcoded Supabase key in client JavaScript — combined
with RLS off, the public key became an admin backdoor. **A secret committed even once
lives in git history forever.** Moving it to `.env` later does nothing; the only real
fix is **rotation**.
---
## When this fires
Trigger phrases: *"scan for secrets"*, *"are my keys exposed"*, *"did I commit an
API key"*, *"is my .env safe"*, *"rotate keys"*, *"about to open-source this"*,
*"pre-launch secret check"*.
Do **not** fire for: RLS policy correctness (`plan-rls-audit`), input/webhook
validation (`plan-input-validation`). This skill owns *credential exposure and
key scoping* specifically.
---
## Why a dedicated skill
A grep finds strings. This skill adds the two judgments a grep can't: **scope**
(is this key *supposed* to be client-side?) and **permanence** (is it in history,
making relocation insufficient?).
---
## The audit
### A · Pattern scan (working tree) [LOW freedom — run exactly]
Search outside `.env*` and server-only contexts for:
- Prefixes: `sk_`, `pk_`, `whsec_`, `service_role`, `eyJ...`, `AKIA`, `API_KEY`,
`SECRET`, `TOKEN`, long random blobs.
- Supabase: anon vs `service_role`.
- Any key in files that ship to the browser.
### B · Scope classification [HIGH freedom]
- **Safe client-side:** Supabase **anon**, Stripe **publishable** (`pk_`), public
analytics keys — note the protector dependency (RLS, Stripe design).
- **Never client-side (Critical if exposed):** **service_role**, Stripe **secret**
(`sk_`), **webhook secret** (`whsec_`), AWS secrets, DB URLs.
- **`NEXT_PUBLIC_` / `VITE_` / `EXPO_PUBLIC_` trap** — bundled into client.
### C · Git history (rotate vs relocate) [HIGH freedom]
- Ever in committed history → **rotate** (relocation is theater).
- Scrubbing (filter-repo/BFG) is secondary — rotation first.
### D · Deployment env config (Vercel / AWS) [HIGH freedom]
- Secrets in platform env store, not baked into build.
- `.env.example` not committed with real values.
- No secrets as build args (persist in image layers).
### E · `.gitignore` & hygiene [HIGH freedom]
- `.env*` ignored; no secrets in README, comments, test fixtures.
---
## Procedure [HIGH freedom]
1. **Scan** working tree (A), classify scope (B).
2. **Check history** for every credential (C).
3. **Review deploy + hygiene** (D, E).
4. **Score.** Never-client in client bundle or history = Critical.
5. **Phase.** **Emit `plan-secrets-audit.md`. End the turn.**
---
## Guardrails [LOW freedom — run exactly]
- **Plan only.** No rotation, history rewriting, or env edits.
- **Never print the secret.** Type + location + last 4 chars at most.
- **Rotate beats relocate — always say which.**
- **Don't assume safe-client keys are fine.** Hand anon-key + no-RLS to
`plan-rls-audit`.
- **Order:** rotate → update env store → redeploy → (optional) scrub history.
## Self-critique before the burndown [LOW freedom — do not skip]
1. **evidenced-not-assumed** — type + location + last 4; never the secret value
2. **plan-only** — no rotation, scrub, or env edit this pass
3. **phase justified** — history → rotate is Phase 1, not "move to `.env`"
4. **right-owner** — anon-key + no-RLS → `plan-rls-audit`; generic OWASP → `plan-security-audit`
5. **no-false-safety** — relocate-without-rotate is theater; safe-client still needs its protector
---
## Report template — `plan-secrets-audit.md`
```markdown
# Secrets & Key-Scope Audit — <repo>
_Audit-only. No key is rotated, moved, or scrubbed until each phase is approved._
## Scope
- Scanned: working tree ☐ git history ☐ deploy env (Vercel/AWS) ☐
- Assumptions / not inspected: …
## Verdict
| Severity | Count | Worst case |
|----------|-------|-----------|
| Critical | n | never-client secret exposed / in history |
| High | n | committed .env values, public-prefix leak |
| Medium | n | shared-env keys, hygiene |
## Findings
| # | Key type | Location | Scope bucket | In history? | Action | Sev |
|---|----------|----------|--------------|-------------|--------|-----|
| S1 | Supabase service_role | lib/admin.ts:4 | never-client | YES | ROTATE now | Crit |
| S2 | Stripe secret sk_ | api/pay.ts:2 | never-client | YES | ROTATE now | Crit |
| S3 | Supabase anon | client.ts:6 | safe-client | n/a | OK if RLS holds → plan-rls-audit | — |
| S4 | DB URL | .env.example | never-client | YES | ROTATE + remove from example | High |
## Phased burndown
- **Phase 1 — Rotate exposed never-client secrets** → dashboard rotation + Vercel/AWS env update (grace window)
- **Phase 2 — Relocate clean secrets** → move to env store, fix NEXT_PUBLIC_ leaks
- **Phase 3 — Hygiene** → .gitignore, remove committed examples, CI secret scanner
- **Phase 4 — (optional) Scrub history** → filter-repo/BFG, after rotation
## Execution handoff
Approve a phase to run it. Cross-hand safe-client keys to `plan-rls-audit`.
Add a pre-commit secret scanner (`create-hook`) so this can't regress.
```
---
## Chains with
- **Security spine** — credentials layer (**this skill**); cross-hand to
`plan-rls-audit` for anon-key safety.
- **`create-hook`** — pre-commit secret-scanning hook as regression guard.
- **Execution:** provider dashboards, Vercel/AWS env, `audit-security`.
- **Verify:** re-scan working tree + history; confirm rotated keys are dead.
> Plan with a strong model; execute with `composer-2.5-execution.mdc` riding
> along. Rotation is irreversible-ish — the plan says *which* keys; the rule
> constrains *how* and *in what order*.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!