← All advisories
VV-043criticalBroken access control2026-09-25

UpGuard: 16,326 Supabase databases had readable tables

UpGuard checked about 300,000 websites that use Supabase and found 16,326 databases with tables anyone could read. Over half showed signs of personal data, and a smaller share held passwords or auth tokens.

01.

What it is

UpGuard gathered about 300,000 domains with signs of Supabase use from their public JavaScript, then asked each database for a “users” table. 16,326 databases exposed readable tables. Judging by table schemas, over half had indicators of personal data and a smaller percentage included passwords or authentication tokens.

Cases UpGuard verified include a users table with 65,467 people on an Indian adult-content site, over 100,000 customers of a US valet service, 25,000 users of an African government’s consulate, and nearly 5,000 users of a Canadian immigration service, 884 of them with plain-text passwords. UpGuard notified owners where it found a significant exposure.

UpGuard says Supabase now enables RLS by default for tables created in the Table Editor UI, but tables created programmatically through the API, which is how coding agents work, don’t get RLS by default. It points to Claude Code and Codex users as a big part of the population, not just Lovable or Replit apps.

Supabase’s CISO told TechCrunch its projects are “secure by default” and that security is a shared responsibility: “customers control how their own projects are configured.”

02.

How to check

  1. Run the query below in the Supabase SQL editor to list public tables with RLS turned off.
  2. From a logged-out terminal, request /rest/v1/users (or any table) with only your public anon or publishable key. Rows coming back means anyone can read them. An error that hints at a different table name also tells an attacker what exists.
03.

How to fix

  1. Enable RLS on every table in the public schema, including ones your AI agent created through SQL or migrations.
  2. Add policies scoped to the owner, such as auth.uid() = user_id, rather than true. Keep anything truly public in separate tables.
-- Public tables with RLS off
select c.relname as table_name
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relkind = 'r' and not c.relrowsecurity;

-- Fix one table
alter table public.users enable row level security;
create policy "Users read own row" on public.users
  for select to authenticated using ((select auth.uid()) = id);
04.

Sources

Related on VibeVulns
More advisories for this tool
The weekly email

One email a week. Only what changed.

New advisories, CVEs and fixes for Lovable, Bolt, v0, Replit, Base44 and Cursor apps. No roundups of things you already read.

Free. The list is new, so the first issues go out once there are readers. Unsubscribe from any email in one click. We never sell or share addresses.