Supabase Security

The Supabase RLS Mistakes I See in AI-Built Apps

Supabase is one of the best tools for AI builders, but it also creates one of the easiest ways to accidentally expose data. The issue is rarely that RLS is impossible. The issue is that AI-generated apps often make RLS look done before it is actually safe.

J
Josh from SaferCode

RLS is enabled, but not complete

The first mistake is treating “RLS enabled” as the finish line.

A table can have RLS enabled and still be unsafe if the policies only cover SELECT, forget UPDATE or DELETE, use the wrong ownership column, or allow broad access through a team/workspace relationship.

For every table that stores user or customer data, I check each operation separately: SELECT, INSERT, UPDATE, and DELETE. The policy should match the actual product ownership model, not just auth.uid() pasted into the first query that worked.

The frontend decides who owns the row

A common AI-generated pattern is to filter rows in the frontend and assume that is enough.

For example, the app queries all records, filters by user ID in JavaScript, and only renders the matching ones. That may look fine in the UI, but it is not a security boundary.

Ownership needs to be enforced in the database policy and server-side code. The browser can improve UX; it should not decide who is allowed to access private data.

Service-role keys leak into places they do not belong

The Supabase service-role key bypasses RLS. That is useful for trusted server code and dangerous everywhere else.

I look for service-role keys in client bundles, frontend environment variables, logs, old commits, serverless functions that do not need them, and AI prompts that may have copied them into generated code.

If a service-role key has leaked, deleting it from the file is not enough. Rotate it, check deployment logs, inspect Git history, and reduce the number of places that can access it.

Storage policies are treated as an afterthought

Supabase Storage needs the same level of attention as tables.

AI-built apps often create a bucket, make uploads work, and forget to decide who can read, write, list, or delete files. That can expose profile documents, generated media, invoices, exports, or customer uploads.

I check bucket visibility, signed URL usage, path ownership, MIME validation, file size limits, and whether users can enumerate files they do not own.

Joins and team access create policy gaps

The simple case is one user owns one row. Real products rarely stay that simple.

As soon as you add organizations, teams, invites, admins, shared projects, or subscriptions, the policy model gets harder. AI often writes a policy that handles the first user and ignores the team model that appears later.

I test the relationships explicitly: member, non-member, admin, removed member, invited but not accepted, cancelled subscription, and cross-organization access.

The practical way to review Supabase before launch

For an AI-built app, I start with the tables that contain private data or control access to paid features. Then I verify RLS operation by operation and test with at least two real users.

The goal is not to write clever policies. The goal is to make the safe path obvious and the unsafe path impossible.

If you want a Supabase security review before launch, email me at josh@safercode.dev with your staging URL, schema/policy exports, and repo access details.

Want me to review your AI-built app?

Send your staging URL, repo context, and what you are trying to launch to josh@safercode.dev. I will tell you which review path fits best.