LatchDog

← Guides

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):

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

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.

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.