Lovable and Bolt app security checklist: 4 checks before you launch

Updated 27 October 2026 · By Tidyship · 7 min read

Before you launch an app built with Lovable, Bolt or another AI builder on Supabase, check four things:

  1. Row Level Security is on for every table, and each rule limits users to their own rows.
  2. No secret key (Supabase secret or service_role, Stripe secret, OpenAI) is in your website's code.
  3. Paid features unlock only from a Stripe webhook that verifies Stripe's signature, never from the "thank you" page.
  4. Admin pages are locked by a check in the database or on the server, not just a hidden button.

You can check all four yourself in about 30 minutes. Here's how.

Why AI-built apps get these wrong

AI builders are very good at making an app work. Security problems don't show up when you click around your own app, so they're easy to miss.

In 2025 a researcher scanned 1,645 apps from Lovable's public showcase. In 170 of them, about 1 in 10, anyone could read or change database tables using only the public key that every visitor's browser already has. It was published as CVE-2025-48757. Lovable added a security scan, and the researcher noted at the time that it checked whether a rule existed, not whether it was correct.

Automated scanners are worth running. But they can't know which data in your particular app should be private, or how your payments are meant to work. That's what these four checks cover.

1. Can strangers see or change your users' data?

What goes wrong: Supabase lets your website talk to the database directly, using a public key. That key is meant to be public. Row Level Security (RLS) is what stops one person from reading everyone's data. If RLS is off for a table, or a rule says using (true), anyone can read that table.

Check it yourself (5 minutes):

  1. In Supabase, open Advisors → Security Advisor. Treat "Table publicly accessible", "Security policy allows unrestricted access" or "View bypasses row-level security" as red flags.
  2. Create two test accounts. Add something as account A, then sign in as account B and look for A's data anywhere in the app.
  3. For the full picture, run the two SQL queries in our guide Is your Supabase data public?

Fix prompt for Lovable or Bolt

"Enable Row Level Security on every table in the public schema. Add policies so each user can only select, insert, update and delete their own rows (where user_id equals auth.uid()). Don't use 'using (true)' on any table that holds personal data. List every table and policy you changed."

2. Are secret keys sitting in your website's code?

What goes wrong: anything your website uses in the browser, every visitor can see. When a database query fails, a quick "fix" is a key that skips the security rules, and that key ends up in the browser.

  • Must never be in the browser: the Supabase secret key (sb_secret_…) or the older service_role key, the Stripe secret key (sk_live_… or sk_test_…), and AI keys such as OpenAI's (sk-proj-…).
  • Fine in the browser: the Supabase publishable key (sb_publishable_…) or the older anon key, and the Stripe publishable key (pk_…).

Check it yourself (10 minutes):

  1. Open your live site in Chrome, press F12 and choose the Sources tab. Reload the page, then press Ctrl+Shift+F (Cmd+Option+F on a Mac) to search every script. Search for sb_secret_, sk_live_, sk_test_ and sk-proj-.
  2. If you use the older service_role key, copy its last 10 characters from Supabase (Project Settings → API Keys) and search for those. The key is encoded, so searching for the words "service_role" won't find it.
  3. In your code, any setting whose name starts with VITE_ or NEXT_PUBLIC_ is copied into the browser. None of them should hold a secret.

Found one? Follow our guide to fixing an exposed Supabase key. Moving the code isn't enough: a key that was public has to be replaced.

3. Can people get your paid features without paying?

What goes wrong: the app switches on Pro when the "thank you" page loads, so typing that address unlocks Pro for free. Or the Stripe webhook believes any message sent to it, so anyone can fake a payment.

Check it yourself (10 minutes, in Stripe test mode):

  1. Sign in as a test user, start checkout, then close it without paying. Type your success page's address (often /success) into the browser. If the account becomes Pro, that's a red.
  2. Open your webhook function (often supabase/functions/stripe-webhook). Look for constructEventAsync or constructEvent. If the code reads the message with req.json() and never checks the signature, anyone can fake a payment.
  3. Cancel a test subscription. Pro should switch off.

Fix prompt for Lovable or Bolt

"Remove any code on the success page that changes the user's plan. Only the Stripe webhook Edge Function may change a plan, after verifying the Stripe-Signature header with the webhook signing secret using constructEventAsync. Store each Stripe event ID and skip events already processed. Also remove Pro when a subscription is cancelled."

For working code and how to test it, see Users getting Pro without paying?

4. Can anyone open your admin pages?

What goes wrong: the Admin link is hidden from the menu, but the page itself opens for anyone who types /admin. Or the app decides who's an admin from something stored in the browser, such as localStorage, which any user can edit.

Check it yourself (5 minutes):

  1. Sign in as a normal test user and type your admin address (/admin, /dashboard/admin and so on) into the browser. You should be sent away, and no data should load.
  2. Check where the app gets the user's role. It should come from the database, for example a role column in profiles that users can't change themselves, not from localStorage or the URL.
  3. Check that admin data is also protected by database rules. A locked page doesn't help if the data behind it is open.

Fix prompt for Lovable or Bolt

"Protect every admin route. Read the signed-in user's role from the profiles table, not localStorage, and redirect non-admins away. Make sure users can't change their own role column, and that admin-only data is only readable by admins through RLS policies."

Run the checks again after big changes

When you ask an AI builder for a new feature, it can add tables, loosen rules or rewrite your payment code. Re-run these checks after any change that touches sign-in, payments or the database, and before every big launch or demo.

Questions

Is the Supabase anon or publishable key supposed to be in my website?

Yes. It's designed to be public, and Row Level Security is what protects your data. The secret key (or the older service_role key) is the one that must never be in your website.

Doesn't Lovable's security scan already check this?

Run it: it catches real problems. But an automated scan can't know which data in your app should be private, or how your payments are meant to unlock features. A rule can exist and still let every signed-in user read everyone's data.

Do I need a penetration test before launch?

Not usually for a first launch, unless you handle sensitive data such as health, financial or children's data, or a customer asks for one. These four checks cover the most common launch problems in AI-built apps.

How often should I run these checks?

Before launch, and again after any change that touches sign-in, payments or the database. AI builders can rewrite rules when you ask for a new feature.

Sources