Your Supabase Database Is Probably Public Right Now
The tables exist. The data is there. RLS is off. Anyone with a browser console can read everything.
170+
Apps affected
18,000
Users exposed
CVE-2025-48757
Assigned CVE
What RLS is and why it matters
Supabase is a hosted Postgres database with a REST API bolted on top. Every Supabase project ships with two API keys: an anon key and a service_role key. The anon key is meant to be public. It goes in your frontend JavaScript, right next to your Supabase project URL. This is by design.
The thing that makes the anon key safe is Row Level Security. RLS is a Postgres feature that lets you attach policies to individual tables. These policies define who can SELECT, INSERT, UPDATE, or DELETE rows, and under what conditions. When RLS is enabled on a table and no policy grants access, the table returns zero rows. The anon key can hit the API all day and get nothing back.
When RLS is disabled on a table, there are no policies. The anon key becomes a master key. Anyone who has it (and everyone does, because it is in the frontend bundle) can query every row in that table. SELECT * FROM users, SELECT * FROM payments, DELETE FROM orders. All of it, from a browser console.
The Lovable breach
In February 2026, a security researcher disclosed that a Lovable-hosted application had exposed the personal data of approximately 18,000 users. The affected users were students and educators from top US universities. The data included names, email addresses, and application-specific records. None of it was protected by RLS.
The breach was not the result of a sophisticated attack. The researcher opened the browser console, extracted the Supabase anon key and project URL from the frontend bundle, and issued a standard REST query against the Supabase API. The database returned everything. No authentication required. No policy to bypass. RLS was simply never turned on.
This was not an isolated case. The subsequent investigation led to CVE-2025-48757, which identified the same missing-RLS pattern across more than 170 applications generated by Lovable. The root cause was consistent: Lovable’s code generation created Supabase tables without enabling Row Level Security, and no step in the deployment pipeline flagged the omission.
Why AI tools get this wrong
Supabase creates tables with RLS disabled by default. The Supabase dashboard now prompts you to enable it, but that prompt only appears if you are manually creating tables through the web UI. AI coding tools do not use the web UI. They generate SQL migrations.
When you tell Lovable, Bolt, or Cursor to “create a users table with name, email, and role columns,” the tool produces a valid CREATE TABLE statement. The table gets created. The app reads and writes to it successfully. There is no error. Postgres does not warn you that RLS is off. The Supabase API does not reject queries to unprotected tables. Everything works, and the data is wide open.
The fundamental problem is that RLS is opt-in, and forgetting to opt in produces no signal. A table without RLS behaves identically to a table with a permissive policy, except the permissive policy was intentional and the missing RLS was not. The AI tool does not distinguish between these two states because, from the perspective of “does the code work,” they are the same.
There is a second key that makes this worse: the service_role key. This key bypasses RLS entirely, regardless of what policies you have configured. If your service_role key leaks into the frontend or a public repository, RLS does not matter. Every table is fully readable and writable. AI tools sometimes use the service_role key in client-side code because it simplifies the data access layer, and again, the code works.
How to check if your tables are exposed
There are two checks. One from inside your database, one from outside.
Check from the database. Run this query in the Supabase SQL Editor. It lists every table in your public schema that has RLS disabled.
SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND rowsecurity = false;
If any table appears in the results, it has no RLS. Anyone with your anon key can query it freely.
Check from the browser. Open your app. Open DevTools. Find your Supabase URL and anon key in the JavaScript bundle (search for supabase.co and eyJhbGci). Then run this in the console:
fetch('https://YOUR_PROJECT.supabase.co/rest/v1/users?select=*', {
headers: {
'apikey': 'YOUR_ANON_KEY',
'Authorization': 'Bearer YOUR_ANON_KEY'
}
}).then(r => r.json()).then(console.log)Replace users with any table name. If you get data back, that table is unprotected. If you get an empty array or a permission error, RLS is working.
How to fix it
Step 1: Enable RLS on every table. Run this for each table that appeared in the query above.
ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;
Once RLS is enabled with no policies, the table returns zero rows to the anon key. This is the safe default. Your app will likely break at this point, which is correct. You need to add policies that grant the specific access your app requires.
Step 2: Write policies. A basic policy that lets authenticated users read their own rows looks like this:
CREATE POLICY "Users can read own data" ON public.users FOR SELECT USING (auth.uid() = id);
Write one policy per operation (SELECT, INSERT, UPDATE, DELETE) per role that needs access. Be specific. A policy that says USING (true) is equivalent to no RLS at all.
Step 3: Verify with the anon key. Run the browser console test again. Confirm that unprotected queries return empty results or permission errors. Test each table. Test with and without a logged-in session. If any table leaks data to an unauthenticated request, the policy is too permissive.
Step 4: Check for service_role leaks. Search your frontend bundle and your git history for your service_role key. It starts with eyJhbGci just like the anon key, but it is longer and has different claims. If it appears anywhere public, rotate it immediately in the Supabase dashboard and replace it in your server-side environment variables. The service_role key should never exist in client-side code.
The takeaway
RLS is not a nice-to-have. It is the only thing standing between your Supabase anon key and full public access to your database. Supabase made this key public on purpose, with the assumption that RLS would gate every query. When AI tools generate tables without enabling it, they remove the gate and leave the key.
CVE-2025-48757 affected 170+ apps. One of them leaked 18,000 user records from US universities. These were not complex attacks. They were GET requests with a key that was already in the page source. The fix is a one-line SQL statement per table, followed by policies that define who can see what. It is not optional.
Run a free surface scan
Talon checks your Supabase configuration, frontend bundles, and public repositories for exposed keys and missing security controls. Passive, read-only, no account required.