Home / Tools / Supabase RLS Checker
Free tool · 2026

Supabase RLS Checker

Test your Supabase Row Level Security configuration in 60 seconds. Find disabled RLS, permissive policies, and the CVE-2025-48757 exposure pattern.

CVE-2025-48757 RLS policies USING(true) detection service_role exposure
TL;DR: Supabase tables have RLS disabled by default. CVE-2025-48757 (May 2025) documented 170+ production apps with fully readable databases because RLS was off and the public anon key — in the client bundle by design — had unrestricted access. Run the three SQL queries below to check your app manually, or use the automated checker at launchreadycode.com for a complete scan in 60 seconds.

What this RLS checker covers

Supabase Row Level Security checking has three components. All three must pass for your database to be correctly protected.

The automated scanner at launchreadycode.com also checks for service_role key exposure in the client bundle, Edge Function JWT verification, and Storage bucket permissions — gaps that SQL queries alone cannot detect.

Run the checker manually — 3 SQL queries

Open your Supabase project, navigate to the SQL Editor, and run these three queries in sequence.

Query 1 — Find tables with RLS disabled

SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY rowsecurity, tablename;

-- Expected: every row shows rowsecurity = true
-- Problem:  any row showing rowsecurity = false is publicly accessible
-- Fix:      ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;

Query 2 — Find tables with RLS on but no policies

SELECT t.tablename
FROM pg_tables t
LEFT JOIN pg_policies p
  ON p.tablename = t.tablename AND p.schemaname = t.schemaname
WHERE t.schemaname = 'public'
  AND t.rowsecurity = true
  AND p.policyname IS NULL;

-- Expected: zero rows (every RLS-enabled table has at least one policy)
-- Problem:  any table returned here is completely inaccessible
--           SELECT returns 0 rows, INSERT/UPDATE/DELETE are rejected
-- Fix:      add appropriate policies (see examples below)

Query 3 — Find permissive USING(true) policies

SELECT tablename, policyname, cmd, qual
FROM pg_policies
WHERE schemaname = 'public'
  AND qual = 'true';

-- Expected: zero rows
-- Problem:  any policy with qual = 'true' allows ALL users to access ALL rows
--           This is the same as having RLS disabled, just harder to spot
-- Fix:      replace USING (true) with USING (auth.uid() = user_id)

How to interpret your results

Query resultStatusAction required
rowsecurity = false (Query 1) EXPOSED Enable RLS immediately. Any visitor can dump this table using the anon key from your bundle.
Table with RLS on, no policies (Query 2) BROKEN Add policies. Your app is silently returning empty results or rejecting writes for this table.
USING (true) policy (Query 3) PERMISSIVE Replace with owner-scoped policy. RLS is technically on but provides no access restriction.
All queries return zero rows PASS RLS configuration looks correct. Run the automated scanner for service_role and Edge Function checks.

Run the automated checker

The SQL queries above check your database configuration directly. The automated scanner at launchreadycode.com tests what is publicly exposed from your live app — the same view an attacker has — and covers additional gaps that SQL queries cannot detect:

Free scan: Paste your app URL at launchreadycode.com. No code access, no signup. You get a Launch Readiness Score /100 across security, reliability, performance, and monitoring in 60 seconds. No login required.

How to write a correct Supabase RLS policy

The pattern that correctly scopes each user to their own data:

-- Enable RLS first
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- SELECT: each user sees only their own posts
CREATE POLICY "posts_select_own"
  ON posts FOR SELECT
  USING (auth.uid() = user_id);

-- INSERT: each user can only create posts as themselves
CREATE POLICY "posts_insert_own"
  ON posts FOR INSERT
  WITH CHECK (auth.uid() = user_id);

-- UPDATE: each user can only update their own posts
CREATE POLICY "posts_update_own"
  ON posts FOR UPDATE
  USING (auth.uid() = user_id)
  WITH CHECK (auth.uid() = user_id);

-- DELETE: each user can only delete their own posts
CREATE POLICY "posts_delete_own"
  ON posts FOR DELETE
  USING (auth.uid() = user_id);

-- Verify no permissive policies remain
SELECT tablename, policyname, qual
FROM pg_policies
WHERE schemaname = 'public' AND qual = 'true';
Replace posts with your table name and user_id with the column that references auth.users.id. Run one policy block per table, then test all the queries in your app to confirm they still return the expected data.

Run RLS checker free

URL-based scan. No code access. No signup. Checks RLS, service_role exposure, Edge Function auth, Storage buckets, and 40+ additional vectors.

Run RLS checker free

Frequently asked questions

What does the RLS checker test?

Three categories: tables with RLS disabled (publicly accessible), tables with RLS on but no policies (inaccessible to all users — a different kind of misconfiguration), and permissive USING (true) policies that grant all users access to all rows. The automated scanner also checks for service_role key exposure in the client bundle, Edge Function JWT verification, and Storage bucket permissions.

My tables show rowsecurity=false — what does that mean?

rowsecurity = false means Row Level Security is disabled on that table. Every row is accessible to any request using your Supabase anon key — including unauthenticated requests. The anon key is in your client JavaScript bundle, visible to anyone who opens DevTools. This is the exact vulnerability pattern of CVE-2025-48757, which exposed 170+ production apps in May 2025. Fix immediately: ALTER TABLE your_table ENABLE ROW LEVEL SECURITY; then add owner-scoped policies before re-testing.

What is USING(true) and why is it dangerous?

USING (true) is a policy condition that evaluates to true for every user and every row. A policy like CREATE POLICY 'allow_all' ON users FOR SELECT USING (true) means any authenticated user can read all rows in that table. RLS is technically enabled so a surface check passes — but the policy provides no restriction. This is a common pattern in AI-generated Supabase code. Replace with USING (auth.uid() = user_id) to restrict access to each user's own data.

Does enabling RLS break my app?

Enabling RLS without adding policies first will break your app — all SELECT queries will return zero rows and all writes will be rejected. The correct sequence: (1) write your policies, (2) enable RLS, (3) test every query path in your app. The most common post-RLS breakage is admin functionality that needs to see all rows — this requires using the service_role key on the server side, never in client code.

How do I write a correct RLS policy?

The core pattern: CREATE POLICY "read_own" ON table_name FOR SELECT USING (auth.uid() = user_id); This scopes access to rows where the user_id column matches the authenticated user's ID. Write separate policies for SELECT, INSERT, UPDATE, and DELETE. For INSERT, use WITH CHECK instead of USING. For UPDATE, use both. Never use USING (true) unless you intentionally want all authenticated users to access all rows (e.g. a public posts table).

Sources: CVE-2025-48757 (NVD / Matt Palmer, May 2025); Supabase documentation on Row Level Security; OWASP Top 10 2021. This page provides general security guidance, not a certification or guarantee.