A security prompt for vibe coding (copy and paste)
A good security prompt for vibe coding asks your AI builder to audit the app first and change nothing until you approve: RLS on every table, no open or signed-in-only policies, no self-editable role columns, no secret keys in frontend code, and checks in every server endpoint. Paste the prompt below, fix one finding at a time, then verify with SQL and a second account, because the AI can't see your live database settings.
If you built your app with Lovable, Bolt, Cursor, Claude Code or v0, the AI that wrote it can also review it. It just won't do that unless you ask, and a vague "make it secure" gets you vague answers. The prompt below names the specific holes that keep showing up in Next.js + Supabase apps and stops the AI from "fixing" things in ways that make them worse.
This isn't a hypothetical risk. In November 2026 the security firm Crissy Field looked at 34,545 Lovable apps that use Supabase. 24,664 of them showed database records without anyone logging in, and on 7,403 of those the researchers could read email addresses, across 10,560 tables (Techzine's report). Some addresses may be public contacts, and the report blames access-control mistakes rather than a hack. The full background is in our post on the Lovable RLS vulnerability and the 2026 leaks.
What is the best security prompt for vibe coding?
The best one is specific and makes the AI report before it edits. Copy this whole block into your AI builder's chat:
Act as a security reviewer for this app (Next.js + Supabase). Do NOT change any code yet.
Audit the project and give me a numbered list of findings. For each one: file or table,
what is wrong, who could exploit it (anyone on the internet / any signed-in user),
severity (critical / high / medium), and the fix you propose. Then stop and wait for me.
Check for these specifically:
1. Row Level Security: any table in the public schema with RLS disabled.
2. Open policies: any policy with using (true) or with check (true), and any policy that
only checks the user is signed in (auth.uid() is not null, or "to authenticated" with
no ownership check). Treat signed-in-only as PUBLIC, because anyone can sign up and
anonymous sign-ins may be on. Policies should compare a column to auth.uid().
3. Privilege columns: can a user update their own role, is_admin, plan, credits, balance
or similar columns? RLS picks which rows a user can touch, not which columns. Flag any
update policy on a table that holds these columns.
4. Secret keys in the frontend: the service_role key or any sb_secret_ key in client
components, browser code, or any NEXT_PUBLIC_ / VITE_ environment variable.
5. Unprotected server code: API routes, server actions and Supabase Edge Functions that
read or change data without checking who the caller is, or that trust a user id sent
in the request body instead of the session.
6. Storage: public buckets that hold user uploads, and storage policies that let any user
read or overwrite other users' files.
7. Committed secrets: API keys, tokens or .env files in the repo or its git history.
Rules for this review and any fix afterwards:
- List findings first. Make no edits until I approve a specific item.
- Put every database change in a new SQL migration file, apply it, and show me the SQL.
- Never fix an error by disabling RLS, adding using (true), or granting access to anon.
- Never move the service_role or secret key into client code or a NEXT_PUBLIC_ / VITE_ variable.
- If you cannot verify something from the code (for example live database settings),
say "cannot verify from code" instead of guessing.
The last rule matters more than it looks. Without it, models tend to say a table "has RLS enabled" because they saw a migration file, even if that migration never ran.
How do I make the AI fix a finding?
One item at a time. Once you've read the list, send this:
Fix item N only. Write the change as a new migration (for database changes) or a minimal
code edit. Do not disable RLS, add using (true), grant to anon, or touch the service_role
key. Apply the migration, then show me exactly what changed and how I can test it.
Fixing one item per message keeps each change small enough to read. If a fix breaks a page, the AI's first instinct is often to loosen the policy until the error goes away. That is how many of these holes got there in the first place, and the prompt rules out exactly that move.
For the privilege-column finding, the usual fix is to stop users from updating that column at all: move it to a separate table users can't write to, or revoke the table-wide update right and grant update back only on the safe columns, so only your server (using the secret key) can change the rest. Revoking a single column does nothing while the table-wide grant is still there. A good AI will propose one of those. If it proposes "check the role in the frontend," say no.
What an AI self-review can't be trusted for
The prompt helps, but it's still the same tool grading its own work. Two limits:
It can claim fixes it didn't apply. Writing a migration file and applying it to your database are two different steps. Builders sometimes do the first, report success, and the live table is unchanged.
It can't see your live settings. Whether a signed-in-only policy is a leak depends on your Supabase Auth settings: are sign-ups open, is email confirmation on, are anonymous sign-ins enabled? Those live in the dashboard, not the code. The same goes for policies someone added by hand in the SQL editor. The AI reviews what's in the repo, and your database may not match it.
How do I verify the fixes actually worked?
Check the database itself, not the AI's summary. Three checks cover most of it.
1. Run this in the Supabase SQL editor. It lists every public table with RLS still off. An empty result is what you want.
select tablename from pg_tables where schemaname = 'public' and not rowsecurity;
2. Test with a second account. Sign up as a new user in a private window and try to see or change the first user's data: their profile, orders, files. Then try to edit your own role or plan field from the browser. If any of that works, the fix didn't hold. Our guide on how to test Supabase RLS walks through doing this in SQL as well.
3. Search your built frontend for secret keys. If service_role or sb_secret_ shows up anywhere the browser loads, rotate the key in the Supabase dashboard and read what to do when the service role key is exposed.
If you'd rather not do this by hand, GuardLayer has a free, read-only Live check. It connects through Supabase's own OAuth and reads your RLS settings, policies, grants and Auth settings together, never your rows. It flags tables anyone can read, signed-in-only policies that are effectively public because sign-ups are open, and users who can make themselves admin. Each finding comes with the SQL fix and a "Copy fix prompt for AI" button, and "Check again" shows what's now closed. If you built with Lovable specifically, the Lovable security check page explains what it looks at. In Claude Code or Cursor, add GuardLayer as an MCP server and the agent can run the same check itself and confirm each fix.
Prompt or checklist?
Use both. The prompt gets the AI to find and fix problems in code it wrote. The security checklist for vibe-coded SaaS apps is the human version: what you tick off yourself before launch, with a deep-dive for each item.
FAQ
Does this prompt work in Claude Code and Cursor, not just Lovable? Yes. It's plain text and doesn't depend on any builder's features. Tools that can run commands, like Claude Code and Cursor's agent, can also search the git history for committed secrets. Browser-based builders may only see the current code.
The AI says everything is secure. Can I trust that? Not on its own. It can't see whether sign-ups are open, whether email confirmation is on, or whether a migration was applied. Run the SQL query above and test with a second account before you believe it.
Is a policy that only checks the user is logged in safe?
Only if nobody can create an account on their own. On most apps anyone can sign up, so "logged in" means "anyone with an email address." Scope policies to the row's owner with auth.uid() instead.
How often should I run the security prompt? Before launch, and again after any big feature that adds tables, storage buckets or server endpoints. New tables are where RLS most often gets missed.
See which of your Supabase tables anyone can read, free
Connect your Supabase project read-only. GuardLayer checks RLS, policies, grants and functions as they are right now, and gives you the SQL to fix each problem. It never reads your rows and can't change anything.
Check my Supabase projectPrefer to check code instead? Scan your repo, no signup.