ASAASA Standard
Active Phase 1Production Foundation

Supabase RLS Safety

Auth Safety · AUTH-02, AUTH-03 · AUTH-17 · Priority: P0

Why It Matters

Row Level Security (RLS) is the single most important security boundary in any Supabase application. When RLS is missing or misconfigured, every user can read, modify, or delete every other user's data — including billing records, personal information, and admin configurations.

AI code generators frequently create Supabase tables without enabling RLS, or enable RLS but forget to add proper policies. The result is a database that appears to work correctly in development but is completely open in production.

Priority: P0 — Any app without RLS on user-facing tables is not safe for production.

Affected Stack: Supabase, Next.js, any framework using Supabase client


AUTH-02: RLS Enabled on All Tables

The Problem

When you create a table in Supabase, RLS is disabled by default. AI tools like Lovable, Bolt, and Cursor typically generate migration files that create tables without enabling RLS — because the generated code works fine with the service_role key during development.

In production, if a user discovers how to query the Supabase API directly (which is trivial — the URL and anon key are in the client bundle), they can read and modify any row in any table without RLS.

-- ❌ What AI tools typically generate
CREATE TABLE profiles (
  id UUID PRIMARY KEY REFERENCES auth.users(id),
  full_name TEXT,
  email TEXT,
  role TEXT DEFAULT 'user'
);
-- No RLS enabled. Any authenticated user can read all profiles.

The Fix

Enable RLS on every table that contains user data, and add policies that restrict access to the row owner.

-- ✅ Production-safe pattern
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can read own profile"
  ON profiles FOR SELECT
  USING (auth.uid() = id);

CREATE POLICY "Users can update own profile"
  ON profiles FOR UPDATE
  USING (auth.uid() = id);

References


AUTH-03: RLS Policies Have WITH CHECK

The Problem

Even when RLS is enabled, policies without WITH CHECK clauses allow users to insert or update rows they shouldn't own. The USING clause only controls reads — WITH CHECK controls writes.

AI-generated migrations often create SELECT policies but omit INSERT/UPDATE policies with proper WITH CHECK constraints.

-- ❌ Missing WITH CHECK — user can insert rows for other users
CREATE POLICY "Users can insert profiles"
  ON profiles FOR INSERT
  WITH CHECK (true);  -- No ownership check!

The Fix

Every INSERT and UPDATE policy should include a WITH CHECK clause that verifies ownership.

-- ✅ Proper ownership check on insert
CREATE POLICY "Users can insert own profile"
  ON profiles FOR INSERT
  WITH CHECK (auth.uid() = id);

AUTH-17: Storage Bucket RLS

The Problem

Supabase Storage uses the same RLS model as database tables, but AI tools almost never generate storage policies. The default behavior allows any authenticated user to list, read, and upload files in any bucket.

This means user avatars, documents, invoices, and other private files are accessible to any logged-in user.

The Fix

Create storage policies that restrict access by user ID or role, matching the same ownership patterns used in database RLS.

-- ✅ Storage policy: users can only access their own files
CREATE POLICY "Users can read own files"
  ON storage.objects FOR SELECT
  USING (auth.uid()::text = (storage.foldername(name))[1]);

References


Related Checks


Is your app safe? Run Free Scan →