How to write Lovable prompts that pass a security review
A practical checklist for writing Lovable prompts that survive a real security review: RLS, secrets, validation, rate limits, and the boring bits no one prompts for.
12 Jun 2026
How to write Lovable prompts that pass a security review
Most "vibe-coded" apps fail their first security review for the same handful of reasons. The model did exactly what the prompt asked. The prompt just forgot to ask for the boring bits: row-level security, server-side validation, secret handling, rate limits, and audit trails.
This guide is a checklist you can paste into the top of any Lovable build prompt. It is also exactly what LovableForge checks for when you run a critique.
1. Spell out who owns each row
Every table that holds user data needs a clear owner column and a policy that scopes reads and writes to that owner. If you do not say so in the prompt, you will get a public select * policy and a TODO comment.
A good prompt says:
Create a
notestable withuser_id uuidreferencing the authenticated user. Enable row-level security. Add a policy that allows a user to select, insert, update, and delete only their own rows. Useauth.uid() = user_id.
A bad prompt says:
Create a notes table with row-level security.
The first one is testable. The second is a wish.
2. Never store roles on the profile table
If your prompt asks for an is_admin boolean on the user profile, you are one stolen JWT away from a privilege escalation. Always store roles in a separate user_roles table and check them with a security definer function. This is non-negotiable.
create type app_role as enum ('admin', 'user');
create table user_roles (
user_id uuid references auth.users(id) on delete cascade,
role app_role not null,
primary key (user_id, role)
);
Then write has_role(_user_id, _role) as a security definer function and use it in your RLS policies. Lovable will do this correctly if you ask.
3. Validate on the server, not just the form
react-hook-form and Zod on the client are great UX, but a determined user opens DevTools and sends whatever they want. Every server function must validate its input again, with the same schema or a stricter one.
The prompt rule is simple: every paid action validates input with Zod inside the server function before it touches the database.
4. Keep secrets out of the bundle
VITE_* environment variables ship to the browser. That is by design. So if you put your Stripe secret in VITE_STRIPE_SECRET_KEY, congratulations: it is now in every page-source view on the internet.
Tell the prompt explicitly:
All third-party API keys live in Lovable Cloud secrets. The browser only ever sees the Supabase publishable key. Server functions read secrets from
process.env.
5. Rate limit anything that costs money
Any endpoint that calls a paid API (OpenAI, Perplexity, Anthropic, Stripe) needs a quota check before it makes the call. Otherwise one user with a script can drain your budget overnight. LovableForge enforces this in every paid edge function with a check_quota(user_id, action) helper.
6. Write the audit trail before you need it
When you ship a feature that creates a workspace, sends an email, or charges a card, write a row to an events table at the same time. You will never regret it. You will absolutely regret not having it the first time a customer says "I never authorised that charge".
7. Pick destructive defaults
When the prompt says "users can delete their account", default to a soft delete and a 30-day window before hard delete. Make the prompt say so. Cascade deletes you cannot undo are the most common foot-gun in vibe-coded apps.
A prompt template you can steal
Paste this above your feature description:
Security baseline:
- Every user-data table has RLS on and an owner_id column.
- Roles live in a separate user_roles table, checked via a security definer function.
- All server functions validate input with Zod before any database call.
- All API keys live in Lovable Cloud secrets, accessed via process.env in server functions.
- Every paid action runs a quota check before calling the upstream API.
- Every destructive action writes to an audit_events table and uses a soft delete where possible.
- Reject unknown fields in input schemas. Return 400 with a code, not a stack trace.
Run a LovableForge critique on the output and apply the rewrites. You will be amazed how many "small" issues this catches before you ever paste the prompt into Lovable.
Want LovableForge to run a critique like this on your own prompts? See pricing.