Supabase exposes Postgres to the browser through an API (the interface other software calls; tested directly, without the UI in between). The publishable key is in the bundle (the JavaScript your app sends to the browser or ships inside the mobile app; anyone can read it) by design. What keeps one user out of another user's rows is row-level security (database rules that decide which rows each signed-in user may read or write), and a table without it is readable and writable by any role with a grant. (Source: Supabase docs, 2026-09 · verified 2026-09-21)
Three documents, one failure
CVE-2025-48757, rated 9.3, describes an insufficient row-level security policy on generated sites that let unauthenticated attackers read or write arbitrary tables. (Source: NIST National Vulnerability Database, 2025-05-30 · verified 2026-09-21) In January 2026, a key exposed in client-side JavaScript combined with missing policies gave full read and write access to a social platform's database: about 1.5 million API tokens and 35,000 email addresses. (Source: Wiz Research, 2026-02-02 · verified 2026-09-21) Lovable's own Supabase integration docs say every table needs a policy before going live and call missing policies the most common way app data gets exposed.
Three sources, one mechanism: a table was created, by a migration (a scripted change to the database schema that ships with a release and should be reversible) or by a tool, and nobody enabled the policy, or the policy said true.
Why it happens
A table created through the dashboard prompts for it. A table created by SQL, by a migration, or by an agent that writes migrations does not. And when a query fails under the anonymous key, the fastest fix is a policy whose condition is true for everyone, or a switch to the secret key that bypasses policies entirely. Both make the query work. Neither is isolation.
The two items
AC-01 queries the database catalog for every table in an exposed schema and its RLS (row-level security: database rules that decide which rows each signed-in user may read or write) flag. Pass is every table enabled, with exemptions named in the release record (a public read-only lookup table, say). It is automated and runs on every release, because every release can add a table.
AC-02 is the two-user test (sign in as one user, create a row, then try to read or change it as another user; pass is nothing comes back). Sign in as user A and create a row in each table. Sign in as user B and, through the client API with the publishable key, attempt select, update and delete on it. Pass is zero rows on select and a permission error on the writes, for every table. This is the only proof that a policy works; reading it is not.
The related items
SC-01 scans bundles for the secret key. The docs say it must never leave your control and that Supabase returns 401 to a secret key sent from a browser, but that check keys on the User-Agent header and does not cover mobile apps or scripts. AU-03 and AU-04 cover the auth endpoints the same incidents exercised.
The Supabase hub lists the vendor defaults with dates. The access control category has all six items with pass criteria.