Scan free

Claude Code Security Audit Prompt: Copy, Paste, Verify

Below is a Claude Code security audit prompt you can paste as it is. It tells the assistant to read the whole app without changing anything, check the problems that actually show up in Supabase, Stripe and AI-built apps, report each finding with the file, line, severity and evidence, and list what it could not check. It works in Cursor too, and there is a shorter version for Lovable's chat.

If you were looking for a security prompt for vibe coding in general, this is it: nothing in it is specific to one editor. After the prompt: how to save it as a file you can rerun, and an honest section on where an assistant auditing its own code goes wrong.

The Claude Code security audit prompt (copy this)

Paste this into a new session, from your project folder. It is long on purpose: an assistant checks what the prompt points it at.

text
Do a security audit of this whole repository. Read only: do not edit files,
run migrations, install packages or deploy anything until I ask.

How to work:
1. First, map the app. List the framework, where server code runs (API
   routes, server actions, Edge Functions, serverless functions), the
   database and auth provider, payment and AI providers, and every
   environment variable the code reads. Show me the map before any findings.
2. Go through every section below. For each item, either report findings or
   write "checked, nothing found" with the files you read. Never skip an
   item silently, and never call something safe because it looks fine.
3. For each finding give: severity (Critical, High, Medium, Low), file and
   line, the exact lines of code, what a stranger could do with it in one
   plain sentence, and a proof I can run: a curl command, a SQL query or a
   failing test. If you cannot write a proof, mark the finding "unconfirmed".

A. Database (Supabase, Postgres, Firebase)
- Every table in the public schema has row level security enabled. Check
  migrations (supabase/migrations, drizzle, prisma) and any .sql files.
- No policy uses using (true) or with check (true) on a table with private
  or per-user data. No per-user table is open to any signed-in user
  (to authenticated using (true), or auth.uid() is not null).
- Update policies have a with check, so a user cannot hand a row to
  someone else or raise their own role or plan.
- No policy or function decides access from user_metadata or
  raw_user_meta_data. Users can edit those.
- security definer functions set search_path.
- Storage policies tie uploads, updates and deletes to the file's owner.
- The service role or secret key (service_role, sb_secret_) is never used
  in code that runs in the browser.
- Firebase: no "allow read, write: if true" in firestore.rules or
  storage.rules.

B. Secrets
- List every env var with a public prefix (NEXT_PUBLIC_, VITE_,
  EXPO_PUBLIC_, REACT_APP_, PUBLIC_) and say whether its value is meant to
  be public. A secret key under any of these prefixes is Critical.
- No keys hard-coded in source, config, seed scripts or tests.
- .env files are in .gitignore. Search git history for keys that were ever
  committed, even if deleted: run "gitleaks git -v" if it is installed,
  otherwise search "git log -p --all" for sk_live_, sk-, sb_secret_,
  service_role, AKIA and "BEGIN PRIVATE KEY". Any hit means rotate the key.

C. Server routes and ownership
- List every API route, server action, Edge Function and webhook. For each:
  does it check who the caller is, on the server? Does it check that the
  caller owns the record when the id comes from the URL, body or query?
  Flag any route that trusts a user_id, org_id or record id sent by the
  client.
- Admin actions check an admin role on the server, not only in the UI.
- Debug, seed and test routes that would still answer in production.
- Cron routes check a secret.

D. Payments (Stripe)
- Every webhook handler verifies the signature on the raw body
  (stripe.webhooks.constructEvent or the equivalent) before doing anything,
  and stops when verification fails or the secret is missing.
- Prices, amounts, plans and credit counts never come from the client. The
  server looks them up.
- Paid access is unlocked from a server-side record, not from a client flag
  or the checkout success URL.

E. AI features
- No OpenAI, Anthropic, Gemini or other model API key in browser code, and
  no dangerouslyAllowBrowser: true.
- Model output never reaches innerHTML, dangerouslySetInnerHTML, v-html,
  raw SQL, eval, a shell command, or a fetch to a URL the model picked.
- Every endpoint that calls a paid model requires a signed-in user and has
  a rate limit or per-user cap, so a stranger cannot spend my credits.
- Note where user text goes into a prompt that can trigger tools.

F. Web configuration
- CORS: no Access-Control-Allow-Origin: * with credentials, and no code
  that echoes back whatever origin asked.
- Security headers set somewhere in code: Content-Security-Policy,
  Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options or
  frame-ancestors, Referrer-Policy.
- Error responses do not include stack traces.
- Login, sign-up and password reset have a rate limit.

G. Dependencies
- Run "npm audit --omit=dev" (or the equivalent for my package manager)
  and report Critical and High results with the version that fixes each.
- Flag any package in package.json that looks misspelled or invented.

At the end:
1. A table of all findings, most severe first.
2. A section called "What I could not check": everything that depends on
   live state, such as policies added in the Supabase dashboard, auth
   settings, Stripe and hosting dashboards, env vars set on the host, the
   headers the production site really sends, and backups. For each, give
   me the SQL query, command or dashboard page to check it myself.
3. Stop and wait for me to choose what to fix.

Each item maps to a section of the vibe coding security checklist, which has the manual check and the fix.

How to run the security prompt for Claude Code

Use plan mode. Per Claude Code's permission modes docs, plan mode "tells Claude to research and propose changes without making them": it reads files and can run commands, but edits stay blocked until you approve a plan. Press Shift+Tab until you see plan mode, or put /plan in front of the prompt. It may ask before running npm audit or git commands.

Start a fresh session. A session that just built the feature has its own reasoning in front of it, and that reasoning is what you want checked.

When the report comes back, read "What I could not check" first. If it is empty, the run was not careful: a repository never shows your live database or dashboard settings.

Save it as a file: a security self audit prompt .md

SECURITY_AUDIT.md in the repo

The simplest version works in any tool. Save the prompt above as SECURITY_AUDIT.md in the root of your project, commit it, and type:

text
Read SECURITY_AUDIT.md and follow it exactly.

A Claude Code slash command

A Markdown file in .claude/commands/ becomes a command named after the file. Per the Claude Code skills docs, "Custom commands have been merged into skills", but .claude/commands/ files keep working, and one file is the easiest start.

Create .claude/commands/security-audit.md:

markdown
---
description: Read-only security audit of the whole repository
argument-hint: [optional folder or area to focus on]
---

Extra focus for this run, if any: $ARGUMENTS

(paste the full audit prompt from above here)

Now the security audit prompt Claude runs is the same every time. Type /security-audit, or /security-audit supabase/ to focus it; $ARGUMENTS becomes whatever you type after the name. The skill version of the same file is .claude/skills/security-audit/SKILL.md.

A Cursor rule

Per Cursor's rules docs, project rules live in .cursor/rules/ and "must use the .mdc extension"; a plain .md file there is ignored. A rule with alwaysApply: false and no description or globs applies only when you @-mention it, so it won't load into every chat.

Create .cursor/rules/security-audit.mdc:

markdown
---
alwaysApply: false
---

(paste the full audit prompt from above here)

Then type @security-audit in Cursor's chat.

What about /security-review?

Claude Code also ships a built-in /security-review command. Its commands reference says it analyzes "the changes on your current branch", reviewing the diff between your branch and the default branch on origin. Good before each merge. This prompt does a different job: it reads the whole app, including code written before you thought about security.

The Lovable security prompt (short version)

Lovable has its own security tools. Its security docs describe a Quick scan that runs on every publish, a Deep scan you start from the project's Security view, and a conversational review: you ask "Review my app's security" in the chat and Lovable "analyzes your project and responds with a detailed assessment". That review uses credits. What each scan covers is in is Lovable safe?.

Run the Deep scan first. Then switch the chat to Chat mode, which per Lovable's docs "does not change your project's code, files, or database", and paste this:

text
Review my app's security. Do not change any code yet.
1. Every table has RLS on. No policy uses using (true) on private data,
   and no policy reads user_metadata.
2. No secret key (service_role, sb_secret_, Stripe sk_, OpenAI) in
   frontend code or in VITE_ variables. Private keys only in Secrets,
   used from Edge Functions.
3. Every Edge Function checks who is calling, and that they own the
   record they ask for.
4. The Stripe webhook verifies its signature, and prices are set on the
   server, never sent from the browser.
5. AI calls run in an Edge Function, need a signed-in user, and model
   output is never rendered as HTML.
For each problem: the file, what a stranger could do, and how serious it
is. Then list what you could not check from the code.

It is short because Lovable apps share one shape: a Vite frontend talking to Supabase from the browser. Most of the risk sits in the first two items.

The limits of a vibe coding security prompt

A good prompt improves the audit, not the auditor. Four things stay true however you word it.

Same blind spots that wrote the bug. If the model thought using (true) was fine for profiles when it wrote the policy, it is likely to think so again when it reviews it. A fresh session helps a little; it is still the same model.

It reads the repository, not your app. A policy added in the Supabase dashboard, email confirmation settings, a webhook secret set on the host, the headers production really sends: none of that is in the files.

It can say "looks fine" without looking. Asked whether every route checks ownership, it may say yes after reading a few of them. That is why the prompt wants the files it read. A short list means it did not check.

Results vary run to run. The same prompt, run twice, can give two different lists. Neither is the whole picture.

How to make the vibe code security check prompt more reliable

Ask for proof, not opinions. The prompt already asks for a curl command, a SQL query or a failing test per finding. Run them. A finding that can't be shown is a lead, not a bug. Two proofs are worth running whatever the audit says.

Open table, from the outside. With the public key from your frontend, as a stranger would:

bash
curl "https://YOUR_PROJECT.supabase.co/rest/v1/profiles?select=*" \
  -H "apikey: YOUR_PUBLISHABLE_OR_ANON_KEY"

Rows coming back from a private table is a real bug. How to test Supabase RLS runs the same check from the SQL editor, as a second signed-in user, and for writes.

Someone else's record. Make two accounts. Signed in as A, copy a request from the network tab that carries an id, and replay it with an id that belongs to B:

bash
curl -s https://yourapp.com/api/orders/ORDER_ID_OF_USER_B \
  -H "Authorization: Bearer USER_A_TOKEN"

Anything other than 401, 403 or 404 is the ownership problem from section C. An assistant reading code is weakest here, because the bug is a check that isn't there.

Then a few habits:

  • Run it twice, in two fresh sessions, and merge the lists. A finding that shows up once deserves a closer look, not a dismissal.
  • Ask for a failing test before a fix. It proves the bug exists, and the same test proves the fix worked.
  • Fix one finding per change, then rerun its proof. A "fix everything" pass is how new holes get written.
  • Check webhooks and keys by hand anyway. Stripe webhook signature verification shows what verified code looks like; how to hide an API key in the frontend covers where keys belong.

If your assistant argues with an item, the docs win. Supabase's RLS guide says user_metadata "can be modified by authenticated end users". Next.js inlines NEXT_PUBLIC_ values "into any JavaScript sent to the browser"; Vite and Expo say the same of VITE_ and EXPO_PUBLIC_. Stripe says an unverified webhook lets an attacker "send fake webhook events", and to decide the amount on the server so customers can't "choose their own prices". OWASP covers runaway model spending as Unbounded Consumption and unsafe model output as Improper Output Handling.

A deterministic check next to the prompt

A prompt is a good reviewer and a poor inventory. Pair it with something that runs the same rules every time.

A free Tuvio scan of your GitHub repository needs no account. It reads the repository, including its git history, and flags several items from this prompt: Supabase RLS shapes in your .sql migrations (using (true), policies open to any signed-in user, a migration that turns RLS off, user_metadata in a policy; a table that never had RLS turned on isn't flagged yet), secrets committed to the repo or its history, Stripe webhook handlers with no signature check and payment amounts taken from the request, model output reaching HTML, SQL or a shell, an AI client running in the browser with its key in a public env var, and packages on CISA's exploited list, from a catalog refreshed weekly. Scanner versions are pinned, so the same commit gives the same findings. It does not test your live database or dashboard settings, and it does not judge ownership checks in your own business logic; that is where the assistant and the two-account test above still earn their keep. If you work in Claude Code or Cursor, the assistant can also call the same scanner as a tool over MCP, with a free account. Coverage lists what is checked and what isn't. A clean result is a good sign, never a guarantee, so paste the findings into your assistant and ask it to judge each one.

Tuvio: a second look at the app you didn't entirely write.

© 2026 CodexMotive Digital Solutions. Scanning reads your files; it never runs them.

Human supported: people write and test the checks, and a person answers info@tuvio.dev.