Is my Lovable app secure? What Lovable checks, and what to check yourself
Short answer: Lovable gives you a good head start, but no scan can tell you an app is secure, and Lovable's own docs say its scans "cannot guarantee complete security". In practice the question comes down to six checks: (1) a Deep scan has run and its Critical findings are fixed; (2) every table has Row Level Security (RLS) on, with policies that check who owns the row rather than using (true); (3) no view or function hands the public anon key what RLS blocks; (4) no secret key is in the JavaScript your live site serves; (5) a logged-out stranger can't read private data; (6) HTTPS, security headers and no exposed files. Then keep checking after launch, because every AI edit can undo any of them.
What Lovable already checks for you
Lovable has two built-in scans, both free to run (Lovable: Security overview):
- Quick scan: runs in seconds, automatically when you open the publish dialog. It reviews your database access rules (tables without RLS, rules that let everyone through, leaked-password protection turned off), audits your npm dependencies for known critical vulnerabilities, and flags an MCP server your app exposes without sign-in.
- Deep scan: reviews your application code, usually in 3 to 13 minutes, and includes everything in the Quick scan. It looks for users who can see or change data that isn't theirs (including server code that bypasses your database rules), endpoints anyone can trigger, unsafe input and injection, secrets in your source code, payment logic a customer can get around, sign-in flaws, and personal data leaking through errors and logs.
- Guard rails: Lovable detects API keys pasted into the chat and steers them into Secrets, and it refuses
VITE_-prefixed names in Secrets because those are build-time browser values (Lovable: Secrets). Admins can turn on Block publishing with critical issues (available on all plans, off by default), and auto-fix for eligible Quick scan findings. - Trust center (optional, off by default): for apps published with Lovable hosting, Lovable checks the live app's HTTPS, security headers and dependencies after every publish and on a regular schedule, and shows the checks that pass at
/.well-known/trust.html(Lovable: Trust center).
These are good tools, and they see what no outside scan can: your code, your policies and your server logic. Use them first.
Where gaps can remain
- The Deep scan doesn't run on its own. Lovable says it "does not run automatically as you work". You start it from the Security view or the Security center (publishing an app whose MCP integration allows access without sign-in also starts one). Scheduled Deep scans are Enterprise only: weekly or monthly, 1 credit per project per run (Lovable: Security center).
- "RLS is on" isn't "RLS is right". In 2025 researchers reported 303 endpoints with inadequate RLS across 170 of the 1,645 Lovable-built apps they tested (CVE-2025-48757; the CVE record notes that Lovable disputes it, and Lovable's scans have changed a lot since). The lesson still holds: a policy can exist and still let everyone through, so test it the way a stranger would.
- The scans read your project; they don't watch your live site over time. The Trust center does probe the live app, but it lists only the checks that currently pass, and Lovable says it doesn't create findings in the Security center.
- Code that leaves Lovable. If you sync to GitHub and deploy on another host, the publish-time Quick scan is tied to Lovable's publish dialog, so check that deploy yourself.
Check 1: run the Deep scan before you publish
Open the project Security view, click Run deep scan, fix everything marked Critical and read every Warning. Run it again after big changes: new tables, new sign-in flows, payments, file uploads, or anything an AI rewrote in bulk. If you're a workspace admin, turn on Block publishing with critical issues in Workspace settings → Security → Privacy & security.
Check 2: RLS on every table, and no policy that lets everyone through
On Lovable's built-in backend, review policies under More → Database → Manage → Policies (the view is read-only; ask Lovable in the chat to change one) (Lovable: database access). On a connected Supabase project, run these in the SQL editor:
-- Tables with RLS off (rowsecurity = false) are readable with the anon key select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity; -- Policies that let everyone through select tablename, policyname, cmd, roles, qual, with_check from pg_policies where schemaname = 'public' and (qual = 'true' or with_check = 'true');
using (true) is fine for a public product list. It isn't fine for profiles, orders, messages or anything with an email address. The fix is an owner check:
alter table public.orders enable row level security; create policy "owners read their own orders" on public.orders for select to authenticated using ((select auth.uid()) = user_id);
More detail, and the outside test, in Supabase RLS for vibe-coded apps. Supabase's docs also show an event trigger (ensure_rls) that turns RLS on for every new table in public; it only covers tables created after you add it (Supabase: event triggers).
Check 3: views and functions that skip RLS
Views bypass RLS by default, because they usually run as their owner, postgres (Supabase: Row Level Security). A view an AI added for a leaderboard or admin page can return every row of a table whose own policies are perfect. On Postgres 15+ the fix is alter view public.your_view set (security_invoker = true);, or revoke it from the API roles. The full method is in Supabase views bypass RLS.
Functions are the same trap. Postgres grants EXECUTE on new functions to PUBLIC by default (PostgreSQL: Privileges), so a security definer function in public can be called by anyone at /rest/v1/rpc/ with the anon key, with its owner's rights. Supabase's Security Advisor flags these as lints 0028 and 0029. List them:
select p.proname as function_name,
has_function_privilege('anon', p.oid, 'execute') as anon_can_execute,
has_function_privilege('authenticated', p.oid, 'execute') as authenticated_can_execute
from pg_proc p
join pg_namespace n on n.oid = p.pronamespace
where n.nspname = 'public' and p.prosecdef
order by 1;
-- If the browser doesn't need to call it:
revoke execute on function public.your_function from public, anon, authenticated;
Check 4: no secret keys in your live JavaScript
In a Lovable app, anything prefixed VITE_ is compiled into the JavaScript every visitor downloads. VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY are meant to be public; your RLS protects the data. Lovable also keeps .env committed so builds can read those values, so never put a real secret in .env. Secret keys (Supabase service_role or sb_secret_, OpenAI, Anthropic, Stripe sk_live_) belong in Secrets and are used only from server-side code.
Check the site that's actually live, not just the code: open it in a logged-out browser, open DevTools, and search the loaded scripts for service_role, sb_secret_, sk_live_, sk-proj- and sk-ant-. If you find one, fix the code first, then create a new key, deploy it, and only then revoke the old one. See exposed API keys in JavaScript bundles.
Check 5: test as a stranger
Only test your own project. Use the anon or publishable key your site already ships:
curl "https://YOUR-PROJECT-REF.supabase.co/rest/v1/orders?select=*&limit=1" -H "apikey: YOUR_ANON_OR_PUBLISHABLE_KEY"
Rows coming back for data that should be private means the table (or view) is open, whatever the dashboard says. [] means no rows are visible to anon, or the table is empty, so test with a table that has rows. A 401 or 42501 means anon can't reach it at all.
Check 6: HTTPS, headers and exposed files
Make sure HTTP redirects to HTTPS, the six common security headers are set (security headers for AI-built apps), production source maps aren't public, and /.env and /.git/ don't download from your domain. If you host on Lovable, its Trust center shows several of these; on another host, check them there.
Paste-in fix prompt for Lovable
Review this app's data security before I publish. Please: 1. List every table, view and function in the public schema. For every table, say whether Row Level Security is on and show its policies. 2. Flag any policy that uses "true" for anon or authenticated on data that should be private, and replace it with an owner check such as (select auth.uid()) = user_id. 3. For views the browser reads, set security_invoker = true. For views and security definer functions the browser doesn't need, revoke access from public, anon and authenticated. 4. Check that no secret key (service_role, sb_secret_, OpenAI, Stripe secret) is in frontend code or in a VITE_ variable. Move any you find into Secrets and call them only from server-side code. 5. Do not disable RLS, do not grant all privileges to anon, and do not use the service_role key in the browser to make anything work. Show me the changes and the SQL first, and explain who can read what afterwards.
Review what Lovable changes, run a Deep scan, then repeat checks 4 and 5 against the published site.
After launch: keep checking
Lovable's own best-practices page puts it well: "Security is an ongoing process", and reviewing your policies and server code regularly "helps prevent regressions over time" (Lovable: security best practices). A prompt to "fix the profile page" can add a view, loosen a policy or inline a key long after your last Deep scan. Your options: re-run the Deep scan after every meaningful change, schedule it if you're on Enterprise, script outside checks in CI, or use an outside monitor that re-checks the live site and tells you what changed. The vibe coding security checklist is a short version to run after every deploy.
What LatchDog does here (and doesn't)
LatchDog keeps watching your live app after launch. Starter ($19/month, up to 3 apps) and Pro ($39/month, up to 10 apps) re-scan it automatically every day and after every deploy (triggered by a deploy hook from your host or CI), and email you only when something changes. Nothing to install: you verify your domain once. It works on any Lovable app, hosted by Lovable or elsewhere, from the outside. It's meant to sit alongside Lovable's scans, not replace them.
- Free check, no sign-up: security headers, HTTPS and the HTTP-to-HTTPS redirect, leaked secret keys in your JavaScript and source maps (shown masked), public source maps, and exposed
.env/.gitfiles. It does not test your database. - Deep scan, only after you verify you own the domain (DNS TXT record, a meta tag in your homepage's HTML, or a
/.well-known/latchdog.txtfile): using the anon key found in your own JavaScript, it sends oneSELECT ... LIMIT 1to each of up to 15 tables or views your site's code names with.from("...")and records only whether a row came back. Row contents are discarded and never stored. It also looks forservice_role/sb_secret_and other provider secrets, and permissive CORS. Every finding comes with a fix prompt you can paste into Lovable. - Plans: the Free plan includes the free check and one deep scan after verification. Starter is $19/month for up to 3 apps and Pro is $39/month for up to 10 apps; both deep-scan every day and after every deploy, and email you only when something is new, worse or fixed. A one-time $9 Full Report gives you one more full deep scan without subscribing, and the $9 comes off your first month if you subscribe within 30 days. Not happy? Email support@latchdog.com within 7 days of a payment and that payment is refunded in full.
- Limits: it can't see your code, policies, functions, grants or payment logic (that's what Lovable's Deep scan and Supabase's Security Advisor are for), it doesn't call
/rest/v1/rpc/functions, on hosted Supabase it won't test a table your code never names, and it can't check what one signed-in user can see of another's data. It's an outside-in check, not a penetration test or a guarantee.
A sensible setup for a Lovable app: the Quick scan on every publish, a Deep scan before launch and after big changes, Security Advisor after migrations if you use your own Supabase project, and an outside monitor on the live site for everything that changes in between.
Keep watching your app, not just checking it once
LatchDog keeps watching your live app after launch. Starter ($19/month, up to 3 apps) and Pro ($39/month, up to 10 apps) re-scan it automatically every day and after every deploy (triggered by a deploy hook from your host or CI), and email you only when something changes. Nothing to install: you verify your domain once.
Start with the free check (headers, HTTPS, leaked keys in your JavaScript, source maps, .env/.git). Open-table checks run after you verify your domain. See plans
Free, read-only public check: no sign-up, no card. Headers, HTTPS, leaked keys in your JavaScript (always masked), public source maps and exposed .env/.git files. Supabase table checks run after you verify you own the domain.
Want the open-table check too? Get my free deep scan: sign in with your email (no password) and verify you own the domain. Your first deep scan is free and adds CORS, more of your JavaScript and, if your app uses Supabase, whether anyone can read its tables and views with your public key.