How we use AI in our process

Learn more
All insights
Engineering··10 min read

Lovable and Vibe Coding Security: 8 Risks to Check Before Launch

Is your Lovable, Cursor or Bolt app secure? A web app security checklist for vibe-coded apps: Supabase RLS, leaked keys, IDOR, rate limits, and how to fix each.

Lovable and Vibe Coding Security: 8 Risks to Check Before Launch

With Cursor, Lovable, Bolt and Claude Code, founders can go from idea to working app in days. That part is real.

The catch: an app that works isn't necessarily an app that's secure. Most security problems don't show up when you click through your own product. They show up when someone else goes looking.

This isn't hypothetical. In May 2025, a security researcher scanned 1,645 apps from Lovable's public showcase and found that 170 of them, about 1 in 10, had database tables anyone could read or write using only the public key built into the app. Row Level Security had never been switched on. The issue was assigned CVE-2025-48757 and exposed emails, addresses and, in some apps, API keys. Lovable has improved its defaults since then, but the lesson still applies to any app that talks to its database from the browser.

Is Lovable secure?

Lovable, Cursor and Bolt can produce secure apps, but they don't guarantee it. The platform securing its own servers is different from your app being secure.

Lovable now helps more than it used to: since June 2026 it runs a basic security scan every time you publish, checking for issues like missing RLS policies and database misconfigurations, and it offers a deeper scan before launch. Run both. But automated scans catch common configuration mistakes, not your business logic: whether a cancelled customer loses access, whether an admin endpoint checks roles, or whether an AI-added fallback quietly skips a check. Those are still your responsibility, whichever tool wrote the code. The 8 checks below are where vibe-coded apps most often fall short.

Below are the 8 risks we check most often when reviewing AI-built apps, how to spot each one yourself, and what the fix looks like.

The 8 risks at a glance

#RiskHow bad if missedCan you check it yourself?
1Database open to anyone (missing Supabase RLS)CriticalYes, 5 minutes
2Secret keys in frontend code or GitHubCriticalYes, 10 minutes
3Users can see other customers' dataCriticalYes, with two test accounts
4Admin features protected only in the UIHighPartly
5Payment and subscription checks bypassableHighPartly
6No rate limits on login, signup or AI featuresMedium–HighPartly
7Unsafe file uploadsMedium–HighPartly
8AI-added "fallback" logic that weakens securityHighNeeds a code review

1. Your database is open to anyone (missing Supabase Row Level Security)

What it is: Apps built on Supabase (including most Lovable apps) talk to the database directly from the browser using a public key. That's by design, but it's only safe if Row Level Security (RLS) is enabled on every table, with policies that say who can read and write which rows. Without RLS, anyone who copies the public key from your site can read or change your tables.

How to check: In the Supabase dashboard, open the SQL editor and run:

select tablename, rowsecurity
from pg_tables
where schemaname = 'public';

Any table showing false has RLS switched off. Supabase's Security Advisor (in the dashboard) also flags tables without RLS, and if you built with Lovable, its security scan checks for this too.

How to fix: Enable RLS on every table and add policies that match how your app works. For example, letting users read only their own projects:

alter table public.projects enable row level security;

create policy "Users can read their own projects"
on public.projects for select
using (auth.uid() = user_id);

Enabling RLS without policies blocks everything, so test each feature after changing it. And a policy like using (true) is the same as no protection.

2. Your secret keys are public

What it is: Your app connects to services like OpenAI, Stripe and email providers using secret keys. AI tools sometimes put these keys in frontend code, or .env files get committed to GitHub.

Some keys are meant to be public: Supabase's publishable key (sb_publishable_..., or the older anon key) and Stripe's pk_ publishable key. Others must never reach the browser: Supabase's secret key (sb_secret_..., or the older service_role key), Stripe's sk_ secret key, and OpenAI keys.

How to check:

  • Open your live site, press F12, go to the Sources tab, and search all files for service_role, sb_secret_, sk_live, sk- and secret.
  • Check whether your GitHub repo is public, and whether .env files appear anywhere in its history. A free tool like gitleaks scans the whole history.

How to fix: Move secret calls to server-side code (an API route or Supabase Edge Function). Then rotate every key that was exposed. Deleting the file or the commit isn't enough; the old key has to be revoked. If a legacy Supabase service_role key leaked, Supabase recommends replacing it with a new secret key.

3. Users can see other customers' data (IDOR)

What it is: Customer A logs in, changes an ID in the URL or a request (say /invoices/1042 to /invoices/1043), and sees Customer B's invoice. The login works fine. The app just never checks whether this user is allowed to see this record. Security teams call this an IDOR (insecure direct object reference), and it's one of the most common bugs in web apps.

How to check: Create two test accounts. With account A, open a record and copy its URL or ID. Log in as account B and try to open the same URL. If it loads, you have this problem. Try the same for edits and deletes, not just views.

How to fix: Every read, update and delete must check ownership on the server or in the database (RLS policies, as in risk 1). Hiding a record in the UI isn't a check.

4. Your admin dashboard is protected only in the UI

What it is: The "Admin" link is hidden for normal users, but the API endpoints behind the admin dashboard still accept their requests.

How to check: As a normal user, try opening the admin URL directly. If you can see the browser's network requests, try calling an admin endpoint while logged in as a normal user.

How to fix: Check the user's role on the server for every admin action, not just in the menu. Store roles where users can't edit them; a role field on a profile table that users can update themselves is a common mistake.

5. Payment and subscription logic can be bypassed

What it is: Stripe secures payments, but your app decides who gets paid features. Common problems: paid features unlocked by a flag the browser can change, webhooks that aren't verified, and cancelled subscriptions that never lose access.

How to check:

  • Cancel a test subscription. Does the account lose paid features?
  • Ask your developer: are Stripe webhooks verified with the signing secret, and is a duplicate webhook handled safely?

How to fix: Check subscription status on the server for every paid feature. Verify webhook signatures, handle repeated webhook events without creating duplicates, and update access when payments fail or subscriptions end.

6. No rate limits on login, signup or AI features

What it is: Nothing stops a bot from sending thousands of requests a minute to your login, signup or AI features.

What could happen: Password-guessing on login, floods of fake signups, and, for AI products, an OpenAI bill you didn't expect.

How to fix: Add rate limits on login, signup, password reset and any endpoint that costs you money. For AI features, also set per-user usage caps and a spending limit with your AI provider.

7. File uploads can be abused

What it is: Profile pictures, PDFs and spreadsheets are an attack surface if uploads aren't checked.

How to check: In Supabase Storage (or wherever files live), is the bucket public? Can you open another user's uploaded file by guessing or sharing its URL?

How to fix: Use private buckets with short-lived signed URLs for anything personal. Limit file types and sizes on the server, not just in the upload form.

8. AI-added fallback logic weakens security

This one comes from our own code reviews. AI tools try hard to make code work under every condition, so they add fallbacks everywhere. In security-sensitive places, that's the wrong instinct. We've seen patterns like:

  • const secret = process.env.JWT_SECRET || "dev-secret": if the environment variable is missing in production, the app quietly signs logins with a key anyone can guess.
  • A permission check wrapped in try/catch that allows the action when the check errors.
  • Validation that logs a warning and continues instead of rejecting bad input.

How to fix: Security code should fail closed: if a secret is missing or a check errors, stop and refuse the request. Crashing at startup because JWT_SECRET is missing is better than running without it. This needs someone to read the code; it won't show up in testing.

Your pre-launch web app security checklist

  • RLS is enabled on every Supabase table, with policies tested
  • No secret keys in frontend code; exposed keys have been rotated
  • Tested with two accounts: neither can see or change the other's data
  • Admin actions check the role on the server
  • Paid features check subscription status on the server; webhooks are verified
  • Login, signup and AI features are rate limited; AI spend is capped
  • Uploaded files are private unless meant to be public
  • Someone has read the security-sensitive code, not just clicked through the app

FAQ

Is code written by Cursor or other AI tools secure?

Not automatically. AI tools write code that works for the case you describe, and often skip permission checks, rate limits and safe defaults unless you ask for them. Treat AI-written code like code from a fast junior developer: useful, but it needs a review before launch.

What is Supabase RLS, in plain English?

Row Level Security is a set of rules inside your database that decides which rows each user can see or change. Because Supabase apps talk to the database from the browser, RLS is what stops one user, or a stranger, from reading everything.

Is the Supabase anon key supposed to be public?

Yes. The anon key, and its newer replacement the publishable key (sb_publishable_...), is designed to be in your frontend. It's only safe when RLS is set up correctly. The service_role key and its replacement, the secret key (sb_secret_...), bypass RLS and must never be in frontend code. Supabase is phasing out the older anon and service_role keys in favour of the new ones.

Do I need a full security audit before launch?

Not always. For a first launch, the checklist above covers the issues we see most. If you handle payments, health data or business customers' data, get someone experienced to review the code before you go live.

Build fast with AI. Check before you launch.

Your customers don't care whether your app was built with AI or by hand. They expect their data to be safe. A working MVP proves your idea; a secure one keeps the customers it wins.

If you've built your app with Lovable, Bolt, Cursor or Claude Code, see also when an AI-built app needs a real engineering team, or start with a free Finish Line Scan.

Not sure your app is safe to launch?

Get a free Finish Line Scan. We review your AI-built app's database access, keys, auth and payments, and tell you what to fix before real customers arrive.

Get a free scan