On this page 7 sections
Lovable app security issues come down to six settings: a secret key in the browser bundle, tables with row level security off, policies that let everyone through, public storage buckets, Edge Functions with JWT checks turned off, and no limits on paid actions. UpGuard found 16,326 Supabase databases with readable tables. You can check all six in an afternoon.
None of these are exotic. They’re defaults nobody changed and shortcuts taken to clear an error during development. The scale is what makes them urgent: in its study of exposed Supabase apps, UpGuard checked about 300,000 domains, found 16,326 databases with readable tables, and reported that over half of them held some personal data. It also explains why apps built by coding agents get hit: Supabase turns RLS on for tables made in the dashboard, but tables created through the API, which is how agents work, don’t get it by default.
Lovable’s own scan won’t close that gap for you. Its security overview says the tools “cannot guarantee complete security” and that you are responsible for meeting your app’s security requirements. Take that at its word and check the settings yourself.
Lovable app security issues start in the browser
Everything your app ships to the browser is public. That’s fine for the publishable key (the legacy anon key): Supabase’s guide to API keys calls it safe to expose, because it only reaches what row level security allows. It’s a disaster for a secret key. The same guide says a secret key “bypasses every Row Level Security policy you have” and must never go in a browser, a shipped app or source control.
Secrets reach the browser in two common ways:
- A
VITE_variable. If your project has avite.configfile, it’s built with Vite, and the Vite env docs say any variable prefixed withVITE_is bundled into client-side code at build time. AVITE_SUPABASE_SERVICE_ROLE_KEYorVITE_OPENAI_API_KEYis readable by anyone who opens your site. - A key pasted into a component to make a failing call work. The call works, and the key ships.
This isn’t hypothetical. Escape scanned over 5,600 public vibe-coded apps, about 4,000 of them on Lovable, and its write-up of the method reports more than 400 exposed secrets, noting that Supabase service keys “were often trivially retrievable from frontend bundles”.
Check: open your live site, open the browser’s developer tools, and search all loaded JavaScript for service_role, sb_secret_, sk_live and sk-. Search your Git history for the same strings.
Fix: rotate any key you find, then move the call into an Edge Function that reads the key from Lovable’s Secrets. Lovable’s docs are blunt that API keys “cannot be stored safely in client-side code.”
Row level security decides who reads what
The publishable key is only safe if every table it can reach is locked down. Supabase’s row level security guide states that a table in an exposed schema without RLS is readable and writable by any role with a grant on it. Turning RLS on is step one. The policies are step two, and that’s where Lovable apps leak most.
The history here is instructive. When Matt Palmer scanned Lovable projects in 2025, he found 303 vulnerable endpoints across 170 projects, about 10.3% of the 1,645 he analyzed. His statement also notes that the security scanner Lovable shipped afterward “merely checks for the existence of any RLS policy, not its correctness or alignment with application logic.” A policy of using (true) passes an existence check and still hands every row to every visitor.
Check, part one: open the Security Advisor in your Supabase dashboard, or run supabase db advisors from the CLI. The Advisors docs list the lints that matter most here:
0013_rls_disabled_in_public(ERROR): a table anyone with your project URL can read, edit and delete.0024_permissive_rls_policy(WARN): a policy that allows unrestricted access.0010_security_definer_view(ERROR): a view that bypasses row level security.0002_auth_users_exposed(ERROR): user data exposed through a view.
Check, part two: the advisor can’t tell whether a policy matches your business rules. Create two test accounts. Sign in as the second one and try to read, edit and delete records that belong to the first, through the app and through the REST API with the second user’s token. Anything that succeeds is a leak.
If the policies themselves misbehave, our breakdown of why Supabase RLS stops working walks through the six causes and their fixes.
Storage buckets and Edge Functions leak quietly
Two more settings don’t show up when you look at your tables.
Public storage buckets. Supabase’s storage bucket docs say a public bucket bypasses access controls for retrieving and serving its files. That’s right for logos and marketing images. It’s wrong for ID documents, invoices or anything a user uploads privately. Private buckets enforce RLS on every operation and serve files through signed URLs. Make every bucket that holds user files private, and watch for the 0025_public_bucket_allows_listing lint, which flags buckets whose contents anyone can list.
Edge Functions with JWT checks off. By default, the platform checks the caller’s token before your function runs. The Edge Functions auth docs warn that turning verify_jwt off “to clear a 401 during development removes the platform’s check.” That’s exactly the shortcut an AI builder takes when a function throws an auth error. Search supabase/config.toml for verify_jwt = false. Every function on that list either needs to be public on purpose, like a webhook that verifies its own signature, or needs the check back on.
Rate limits stop at Supabase Auth
Supabase rate-limits its Auth endpoints out of the box. The Auth rate limit docs list defaults such as 2 emails per hour from the built-in email provider and 30 requests per 5 minutes on sign-up, recovery and one-time-code endpoints. The page lists nothing for database queries or Edge Function calls, and Lovable’s security overview doesn’t mention rate limiting at all.
So every function you wrote is unlimited until you limit it. That matters most where each call costs money. OWASP’s guidance on unrestricted resource consumption names resources paid per request, such as email, SMS and phone calls, and recommends spending limits on every provider, or billing alerts where limits aren’t available. A script hitting your “generate summary” function in a loop runs up your AI bill, and one hitting your “send invite” function burns your SMS credit.
Fix: add a per-user limit to every function that calls a paid API, sends a message or charges a card, and set a hard monthly cap in the OpenAI, Twilio or email provider dashboard today.
Lovable’s security scan is a floor
Lovable has invested in security tooling, and you should use it. Its security overview describes two scans. The quick scan runs a fixed set of checks on every publish: tables without per-record access control, rules that let everyone through, storage rules, dependencies. The deep scan reviews all your code for issues in your app’s logic, permissions and data.
Read the limits as carefully as the features:
- The deep scan “does not run automatically as you work.” Run it yourself before launch and after big changes.
- “Only vulnerabilities rated critical become findings.” A high-severity issue can sit there with no finding.
- The tools “do not replace a thorough security review,” in Lovable’s own words.
A clean scan tells you the obvious holes are closed. It doesn’t tell you a user can’t read another user’s orders. That’s what the two-account test is for.
The afternoon checklist
Run these in order. The first three can leak data today; the last three cost money or leak it under specific conditions.
| # | Check | Where | Passes when |
|---|---|---|---|
| 1 | No secret keys in the bundle | Browser dev tools, Git history | No service_role, sb_secret_, sk_live or sk- strings anywhere |
| 2 | RLS on every exposed table | Supabase Security Advisor | No 0013 or 0010 errors |
| 3 | Policies match your rules | Two test accounts | User B can’t read, edit or delete User A’s rows |
| 4 | User files in private buckets | Storage settings | Only public assets in public buckets |
| 5 | JWT checks on user-facing functions | supabase/config.toml | Every verify_jwt = false is a deliberate public endpoint |
| 6 | Limits on paid actions | Functions plus provider dashboards | Per-user limits and a monthly spending cap on each provider |
When to fix it yourself and when to bring someone in:
- Checks 1, 4 and 6 are self-serve. Rotating a key, flipping a bucket to private and setting a spending cap don’t need SQL.
- Checks 2 and 3 need someone who reads SQL if the advisor flags anything or the two-account test fails. Rewriting policies by prompting the AI that wrote them tends to loop.
- Check 5 needs a developer when a function needs its own authorization logic.
- Failing three or more checks usually means the app was never designed with access control in mind, and patching one setting at a time won’t hold. That’s the pattern behind the five production failure modes in Lovable and Bolt MVPs.
Sam’s List, VoiceDrop and 100+ audits: what we check first
Zeroic’s security and performance audits cover more than 100 applications, for clients including Sam’s List, VoiceDrop and AppSheet Training, and they specialize in Bubble and Supabase, where abstraction layers hide what scanners miss. On a Lovable app we start with the six checks above, then go further than a checklist can: we read the code, test auth and data access by hand with signed-in and signed-out users, and trace every path a key or a row could take out of the server.
Every finding comes back verified, ranked by severity, and with repro steps and a fix, within 1-2 weeks. When the fixes go deeper than settings, such as auth rebuilt, calls moved behind server functions and tests added so the next prompt can’t reopen a hole, that work runs as a Code Rescue engagement: a 3-day review, then 2-4 weeks, with the screens your users know left as they are.
Frequently asked questions
- Is Lovable secure enough for a production app?
- Lovable can produce a secure app, but it doesn't guarantee one. Its own security docs say the built-in scans cannot guarantee complete security and that you are responsible for meeting your app's security requirements. The platform gives you the tools; the settings in your Supabase project decide whether user data is exposed, so check them before launch.
- Is it safe that my Supabase key is visible in the browser?
- Yes, if it's the publishable or legacy anon key and every exposed table has row level security with correct policies. Supabase designs that key to be public, so anyone can read it and reach whatever your policies allow. A secret or service_role key in the browser is never safe, because it bypasses every row level security policy you have.
- Does Lovable's security scan check row level security policies?
- It checks for tables without per-record access control and for rules that let everyone through, and it runs a quick scan on every publish. It does not prove your policies match your app's logic, and only vulnerabilities rated critical become findings. Test access yourself with two signed-in accounts before you trust a clean scan.
- How do I find a leaked API key in a Lovable or Bolt app?
- Open your live site, load the developer tools, and search every loaded JavaScript file for strings like service_role, sb_secret_, sk_live and sk-. Then search your Git history for the same strings. Any variable with the VITE_ prefix is bundled into the browser code at build time, so treat everything with that prefix as public.
- Do Supabase apps have rate limiting by default?
- Only on Supabase Auth endpoints such as sign-up, sign-in, one-time codes and the built-in email sender. The Supabase rate limit docs list no default limits for database queries or Edge Function calls, so a function that calls a paid AI model, sends SMS or sends email needs its own per-user limit and a spending cap at the provider.
References
- Supabase - Understanding API keys
- Supabase - Securing your data
- Supabase - Row Level Security
- Supabase - Advisors
- Supabase - Auth rate limits
- Supabase - Securing Edge Functions
- Supabase - Storage buckets
- Vite - Env variables and modes
- Lovable - Security overview
- Matt Palmer - Statement on CVE-2025-48757
- UpGuard - Systemic data exposure in Supabase apps
- Escape - How we discovered vulnerabilities in apps built with vibe coding
- OWASP API Security - API4:2023 Unrestricted Resource Consumption
Prashant Abbi