How to security-check your vibe-coded app with Claude Code
Run one prompt before you launch. It checks your repo for the 20 holes below, shows you the file and line behind each answer, and gives you a fix list, most urgent first. It changes nothing until you say so.
- checks
- 20
- groups
- 5
- prompt
- 1
✦Claude Code
+◈Codex
Paste this at the root of your repo
Open Claude Code or Codex in your project folder and paste the whole thing. It works out your stack first, goes through all 20 checks, and stops with a table and a fix list. Nothing gets changed until you approve it.
Paste at Claude Code / Codex
Do a security audit of this repo before I launch it. Work read-only: do not edit, install, delete, commit or deploy anything until I approve the fixes at the end. Read-only commands (git log, grep, npm audit) are fine.
Step 1. Detect the stack and tell me in a few lines: framework, database (Supabase, Firebase, Postgres, other), auth provider, payments and webhooks, file uploads, hosting. If a check doesn't apply (no uploads, no SQL), mark it N/A instead of skipping it.
Step 2. Go through all 20 checks below, one at a time. Read the actual code and config, not just file names, and note the file and line each answer is based on.
SECRETS
1. No secrets in git history. Search the whole history, not just the current files: git log -p --all for API keys, tokens, private keys and connection strings (sk_live_, sk_test_, service_role, AKIA, ghp_, xox, -----BEGIN, postgres://). Use gitleaks or trufflehog if installed. Check .env files are in .gitignore and were never committed.
2. Service keys on the server only. No secret key (Supabase service_role, Stripe secret, OpenAI, database URL) can reach the browser: check NEXT_PUBLIC_ / VITE_ / EXPO_PUBLIC_ / REACT_APP_ env vars, "use client" files, and anything imported into client components.
3. Rotate keys after any leak. If check 1 or 2 finds a real key, list each one and where to rotate it. Deleting the commit does not un-leak it.
4. 2FA on GitHub, Vercel, Stripe and the database host. You can't see this: mark it MANUAL and tell me where to turn it on.
ACCESS
5. RLS on every table. Supabase: every table in the public schema has row level security enabled AND policies that scope rows to the owner (auth.uid()); check migrations and schema SQL, and flag tables with RLS off, no policies, or policies that are just "true". Firebase: rules are not open (allow read, write: if true). Any other database: every query that returns user data filters by the logged-in user.
6. Auth on every API route. List every API route, route handler, server action, edge/cloud function and tRPC/GraphQL resolver, and say whether it checks the session before doing any work. Hiding a button in the UI doesn't count.
7. Admin checks on the server. Admin-only actions check the role on the server, from the session or the database, never from a client-sent flag, query param, localStorage or a hidden URL.
8. Log every admin action. Admin actions (role changes, deletes, refunds, impersonation) record who, what and when.
INPUTS
9. Validate every input on the server. Request bodies, params and form data are validated on the server (zod or similar) with types and length limits, not only in the form.
10. Parameterized SQL only. No queries built by gluing user input into strings or template literals (raw SQL, .raw(), $queryRawUnsafe, string-built rpc calls).
11. Escape user HTML. No user content rendered through dangerouslySetInnerHTML, v-html, innerHTML or a markdown renderer without a sanitizer such as DOMPurify.
12. Limit upload type and size. Uploads check file type (by content, not just extension) and a max size on the server or in storage rules, and user files are never served back as HTML or SVG from my domain.
EDGES
13. Rate limit login and signup. Login, signup, password reset, OTP/magic links, and any expensive or AI endpoint are rate limited. Say if it relies only on the auth provider's defaults.
14. CORS locked to my domain. No Access-Control-Allow-Origin: * on routes that use cookies or return private data, and no reflecting any origin back.
15. Verify webhook signatures. Every webhook handler (Stripe, Clerk, GitHub, Resend, etc.) verifies the signature against the raw request body before trusting the event, e.g. stripe.webhooks.constructEvent with the webhook secret.
16. httpOnly, secure cookies. Session cookies are httpOnly, Secure and SameSite=Lax or Strict. No session tokens or JWTs in localStorage.
PRODUCTION
17. Random IDs, not 1, 2, 3. Records exposed in URLs or APIs (users, orders, invoices, files) use UUIDs or random IDs, or the route checks ownership so guessing the next ID returns nothing.
18. No stack traces in prod. Errors return a generic message: no err.stack, raw database errors or debug mode in production, and no public source maps unless intended.
19. Audit your packages. Run npm audit (or pnpm audit, yarn npm audit, pip-audit) and list every high and critical issue with the version that fixes it.
20. Back up your database daily. Check what the code and config show about backups (Supabase plan or PITR, scheduled dumps). If you can't tell, mark it MANUAL and tell me how to confirm daily backups and test a restore.
Step 3. Report back in this order:
a) One table with columns: # | Check | Result | Evidence | Fix. Result is PASS, FAIL, UNSURE, MANUAL or N/A. Evidence is file:line or the command you ran. Fix is one line.
b) A prioritized fix list: critical first (leaked keys, tables anyone can read, unauthenticated routes, unverified webhooks), then high, then the rest, with a rough time for each.
c) The things I have to check by hand.
Never mark a check PASS without evidence; if you can't verify it, say UNSURE and why. No generic advice.
Then stop and ask me which fixes to make. Don't change any file until I say yes. When I do, fix them one at a time and show me each diff.Keep your keys yours
- 01No secrets in git historyA key you deleted is still in an old commit.
- 02Service keys on the server onlyIf the browser can see it, so can anyone.
- 03Rotate keys after any leakDeleting the commit doesn’t un-leak the key.
- 042FA on GitHub, Vercel, StripeYour accounts are the keys to every key.
Just this group? Try:
Try it: Secrets
Scan this repo’s whole git history for leaked keys.Decide who can do what
- 05RLS on every tableWithout it, one user can read another’s rows.
- 06Auth checked on every API routeHiding a button doesn’t protect the route.
- 07Admin checks on the serverThe client can lie about who it is.
- 08Log every admin actionSo you can see who changed what, and when.
Just this group? Try:
Try it: Access
List every API route that skips the auth check.Never trust input
- 09Validate every input on the serverClient-side checks are for UX, not safety.
- 10Parameterized SQL onlyNever build a query by gluing strings together.
- 11Escape user HTMLOr someone runs their script on your users.
- 12Limit upload type and sizeOr someone uploads a 2GB file, or a script.
Just this group? Try:
Try it: Inputs
Find every place user input reaches SQL or HTML.Lock the outside doors
- 13Rate limit login and signupOr bots try a million passwords for free.
- 14CORS locked to your domainOnly your site should call your API.
- 15Verify webhook signaturesAnyone can fake a payment event.
- 16httpOnly, secure cookiesSo a script on the page can’t steal a session.
Just this group? Try:
Try it: Edges
Check every webhook handler verifies its signature.Clean up before launch
- 17Random IDs, not 1, 2, 3Counting IDs let anyone guess the next record.
- 18No stack traces in prodError pages shouldn’t map out your code.
- 19Audit your npm packagesOne bad dependency is inside everything.
- 20Back up your database dailyAnd test that a restore actually works.
Just this group? Try:
Try it: Production
Run npm audit and fix anything high or critical.What this is and isn’t. This catches the common holes in apps built fast with AI. It isn’t a penetration test. If your app handles payments, health data or anything regulated, get a real security review before you launch.