Lovable and Bolt app security before launch
AI app builders get you from idea to working app in an afternoon. They don't decide who should be allowed to read which rows. That part is still yours, and it's checkable without writing code.
Lovable and Bolt both generate a frontend and usually wire it to a hosted backend, most often Supabase. The generated app talks to the database straight from the browser. That design is fine and common, but it means your security lives in two places you may never have opened: database policies and server-side functions.
This guide is a launch pass you can run from the builder's chat, the Supabase dashboard and your browser's DevTools. No terminal needed.
1. Know what your app is actually made of
Before checking anything, write down three things. Ask the builder's chat if you're not sure:
- Where data lives. Supabase project, the builder's own database, or something else.
- Where secrets live. Supabase Edge Function secrets, the builder's secrets or environment panel, or (bad) pasted into a file.
- What runs on a server. Edge functions, API routes, webhooks. Everything else runs in the visitor's browser and can be read and modified by them.
Rule of thumb: anything in the browser is public. Code, keys, hidden buttons, "admin" flags. Security has to be enforced on the server or in database policies.
2. Row Level Security on every table
If your app uses Supabase from the browser, the anon (or publishable) key ships to every visitor. That's by design. What protects your data is Row Level Security.
- Open the Supabase dashboard → Table Editor. Every table holding user data should show RLS as enabled.
- Open each table's policies. Look for owner checks like
auth.uid() = user_id. - Delete leftover "allow all" or "authenticated users can read everything" policies from prototyping, unless that table really is shared.
Builders often generate a reasonable first policy and then loosen it when something breaks during a chat session ("fix the error, make it work"). Re-read policies right before launch, not just when they were created.
Recent versions of Lovable include a built-in security scan that flags missing RLS and similar issues. Run it if you have it, then still do the two-account test below. A scanner reports what it can see. The test proves what an attacker can do.
3. The two-account test
- Create two accounts, A and B, in your live or preview app.
- As B, create some private data (a note, an order, a profile field).
- Log in as A in a different browser. Open DevTools → Network.
- Use the app normally and find a request that loads A's data. Look at the URL or request body for an ID.
- Replay it with B's ID (right-click → copy as fetch, edit, run it in the Console).
If B's data comes back, the app has an IDOR (insecure direct object reference). Fix the RLS policy or the server function, then repeat the test. Also try the same requests while logged out.
4. Secrets: what must never be in the browser
Search the deployed site's JavaScript (DevTools → Sources, or Network responses) for sk_, sk-, service_role, sb_secret_ and your providers' key prefixes. Or paste the URL into the free pre-launch check, which does this for same-origin bundles and masks anything it finds.
- Fine in the browser: Supabase anon or publishable key, Stripe publishable key (
pk_), Firebase web config. - Never in the browser: Supabase service_role or secret keys, Stripe secret keys, OpenAI or Anthropic keys, email-provider API keys.
If a secret key is in the bundle, rotate it at the provider first, then move the call into an edge function that reads the key from its secrets store. Deleting the line doesn't help on its own. The old key is already public.
Also treat any key you pasted into the builder's chat as exposed if the project is shared or public. Rotate it.
5. Edge functions re-check who's calling
Edge functions often run with elevated database access. That's useful, but it means the function itself must check:
- That the request has a valid user session (verify the JWT, don't just read a
userIdfrom the body). - That the user owns the record they're touching.
- That expensive calls (AI, email, SMS) are rate limited per user.
Ask the builder: "List every edge function, what it does, and exactly where it verifies the caller's identity and ownership. Don't change anything yet." Then review the answer.
6. Payments
If you added Stripe through the builder, check one thing above all: access must be granted by a server-side event (a verified webhook), not by the browser landing on a success page. See payment success-page bypass and Stripe webhooks in production.
7. Storage buckets
Uploaded files are easy to forget. In Supabase Storage, check each bucket: public or private, and do the policies restrict users to their own folder? Invoices, IDs and private photos should never sit in a public bucket.
8. Before you share the link
- Auth: email confirmation and password reset flows work on the production URL, and redirect URLs are set to the production domain.
- Spend caps on any AI or email API the app calls.
- A privacy policy that matches what you actually collect.
- Remove test accounts and seed data from production.
When to get help
If the app handles health data, children's data, or money moving between users, a checklist isn't enough. Get a qualified human to review it before launch.
The full Last Mile kit includes a paste-in rules block for Lovable, Bolt and Replit (Pro) and "No terminal?" notes on every check that normally needs one. Start with the free preview.
Related guides
- Supabase RLS checklist before launch
If your vibe-coded app talks to Supabase from the browser, Row Level Security is the difference between “users see their own data” and “anyone with the anon key reads the whole table.”
- Leaked API keys in your frontend bundle: how to check
If a secret can call a paid API or bypass your database rules, it does not belong in JavaScript the browser downloads. Here is how to find leaks fast and what to do when you find one.
- Payment success-page bypass: verify payments server-side
One of the most common bugs in AI-built paid apps: the success page grants access. Anyone who types the URL gets the product free. The fix is a few lines of server code.
Get launch notes
$ subscribe --launch-notes # new checks, prompts and shipping tips for vibe coders. No spam.