{"slug": "row-level-security-in-lovable-apps-why-your-database-might-be-public", "title": "Row Level Security in Lovable apps: why your database might be public", "summary": "A security scanner for Lovable apps has found that many AI-generated applications leave their Supabase databases publicly readable because Row Level Security (RLS) is not enabled. The scanner, howsafeismyapp.com, reports that the anon key, which is shipped in the JavaScript bundle, can be used to query any exposed table unless RLS policies are in place. The project recommends enabling RLS and writing policies to restrict access, warning that failure to do so could violate GDPR requirements.", "body_md": "*Originally published on howsafeismyapp.com.*\n\nHere is the uncomfortable truth about most Lovable apps: the database is not behind your login form. It is behind Supabase's API — and whether that API hands out your data to strangers depends on one setting most AI-generated apps never mention: **Row Level Security**.\n\nThis is the most serious finding our scanner sees in the wild. It is also completely fixable in an afternoon. (Practical guidance, not legal advice.)\n\nTraditional apps put a backend server between the browser and the database. Lovable apps usually don't: the browser talks to Supabase's REST API directly, authenticated with the so-called `anon`\n\nkey that is shipped inside your JavaScript bundle.\n\nThat key is *supposed* to be public. Anyone who opens your app has it — and with it, anyone can send requests like:\n\n`GET https://your-project.supabase.co/rest/v1/users?select=*`\n\nWhat decides whether that request returns your entire users table or an empty response is not the key. It is Row Level Security.\n\nRow Level Security is a PostgreSQL feature that Supabase builds on. With RLS enabled on a table, every query is filtered through **policies** — rules that say which rows a given user may read or write. A typical policy:\n\n`SELECT`\n\nrows in `profiles`\n\nwhere `user_id = auth.uid()`\n\n`SELECT`\n\nfrom `payments`\n\nat all via the public API**Without RLS enabled, there is no filter.** The `anon`\n\nkey can read every row of every exposed table. Your login form is decoration; the data was never behind it.\n\nLovable and similar builders generate working code fast, and \"working\" means the app can read and write its own data. Two common paths to trouble:\n\n`USING (true)`\n\n— \"allow everyone\" — that never got removed.Neither produces an error. The app works perfectly. That is exactly why this finding survives into production. Our check page explains what the scanner looks for: [Database readable without a login](https://howsafeismyapp.com/checks/open-database).\n\n`*.supabase.co/rest/v1/...`\n\n— note your project URL and the `apikey`\n\nheader (that's the anon key; it's meant to be visible).`curl`\n\n.Under **Art. 32 GDPR** you are required to have appropriate technical measures for personal data; an openly readable users table is about as clear a failure of that as exists. If real personal data was exposed, **Art. 33 GDPR** (breach notification, 72 hours) becomes the relevant question — a conversation you want to avoid ever needing.\n\nIn the Supabase dashboard:\n\n`auth.uid() = user_id`\n\n), insert only their own, update only their own. Add broader read access only where the product genuinely needs it (e.g. a public feed).`service_role`\n\nkey to the frontend.Then re-run the two-minute check from above. The logged-out request should now return an empty array or an error — both are success.\n\nThe inside view (dashboard says RLS enabled) and the outside view (what a stranger's browser can actually fetch) can disagree — a forgotten table, a too-broad policy, a second Supabase project. The outside view is the one that matters, and it is what a passive scan checks: our scanner requests your app the way any anonymous visitor would and reports which tables answer. It takes about a minute, needs no login, and stores nothing.\n\nNo — it is designed to be public and ships in your JavaScript bundle by design. The protection layer is Row Level Security, not the key. The key that must stay secret is `service_role`\n\n— see [admin key exposed in frontend code](https://howsafeismyapp.com/checks/exposed-admin-key).\n\nEnabling RLS without policies makes tables return empty results via the public API — so your app's own queries stop working until you add policies for them. That is the point: write the policies your app needs, and everything else stays closed. Budget an hour, not a weekend.\n\nCheck the API logs in the Supabase dashboard for anonymous requests to sensitive tables. If personal data of real users was readable, look into the breach-notification duties of Art. 33 GDPR — the 72-hour window starts when you become aware, and honest, early handling counts in your favor.", "url": "https://wpnews.pro/news/row-level-security-in-lovable-apps-why-your-database-might-be-public", "canonical_source": "https://dev.to/hdevv/row-level-security-in-lovable-apps-why-your-database-might-be-public-mj2", "published_at": "2026-08-11 21:49:44+00:00", "updated_at": "2026-08-11 22:17:32.997283+00:00", "lang": "en", "topics": ["ai-products", "ai-tools", "ai-safety", "ai-ethics"], "entities": ["Lovable", "Supabase", "PostgreSQL", "howsafeismyapp.com", "GDPR"], "alternates": {"html": "https://wpnews.pro/news/row-level-security-in-lovable-apps-why-your-database-might-be-public", "markdown": "https://wpnews.pro/news/row-level-security-in-lovable-apps-why-your-database-might-be-public.md", "text": "https://wpnews.pro/news/row-level-security-in-lovable-apps-why-your-database-might-be-public.txt", "jsonld": "https://wpnews.pro/news/row-level-security-in-lovable-apps-why-your-database-might-be-public.jsonld"}}