RLS Policy Audit ๐ด CRITICAL: PROGRESSIVE FILE UPDATES REQUIRED You MUST write to context files AS YOU GO , not just at the end. Write to .sb-pentest-context.json IMMEDIATELY after each finding Log to .sb-pentest-audit.log BEFORE and AFTER each test DO NOT wait until the skill completes to update files If the skill crashes or is interrupted, all prior findings must already be saved This is not optional. Failure to write progressively is a critical error. This skill tests Row Level Security (RLS) policies for common vulnerabilities and misconfigurations. When to Use This Skill After discovering data exposure in tables To verify RLS policies are correctly implemented To test for common RLS bypass techniques As part of a comprehensive security audit Prerequisites Tables listed Anon key available Preferably also test with an authenticated user token Understanding RLS Row Level Security in Supabase/PostgreSQL: -- Enable RLS on a table ALTER TABLE posts ENABLE ROW LEVEL SECURITY ; -- Create a policy CREATE POLICY "Users see own posts" ON posts FOR SELECT USING ( auth . uid ( ) = author_id ) ; If RLS is enabled but no policies exist, ALL access is blocked. Common RLS Issues Issue Description Severity RLS Disabled Table has no RLS protection P0 Missing Policy RLS enabled but no SELECT policy Variable Overly Permissive Policy allows too much access P0-P1 Missing Operation SELECT policy but no INSERT/UPDATE/DELETE P1 USING vs WITH CHECK Read allowed but write inconsistent P1 Test Vectors The skill tests these common bypass scenarios: 1. Unauthenticated Access GET /rest/v1/users?select=*
No Authorization header or with anon key only
- Cross-User Access
As user A, try to access user B's data
GET /rest/v1/orders?user_id=eq.[user-b-id] Authorization: Bearer [user-a-token] 3. Filter Bypass
Try to bypass filters with OR conditions
GET /rest/v1/posts?or=(published.eq.true,published.eq.false) 4. Join Exploitation
Try to access data through related tables
GET /rest/v1/comments?select=,posts() 5. RPC Bypass
Check if RPC functions bypass RLS
POST /rest/v1/rpc/get_all_users Usage Basic RLS Audit Audit RLS policies on my Supabase project Specific Table Test RLS on the users table With Authenticated User Test RLS policies using this user token: eyJ... Output Format โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ RLS POLICY AUDIT โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Project: abc123def.supabase.co Tables Audited: 8 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ RLS Status by Table โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 1. users RLS Enabled: โ NO Status: ๐ด P0 - NO RLS PROTECTION All operations allowed without restriction! Test Results: โโโ Anon SELECT: โ Returns all 1,247 rows โโโ Anon INSERT: โ Succeeds (tested with rollback) โโโ Anon UPDATE: โ Would succeed โโโ Anon DELETE: โ Would succeed Immediate Fix:
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users see own data"
ON users FOR ALL
USING (auth.uid() = id);
- posts RLS Enabled: โ YES Policies Found: 2 Status: โ PROPERLY CONFIGURED Policies: โโโ "Public sees published" (SELECT) โ โโโ USING: (published = true) โโโ "Authors manage own" (ALL) โโโ USING: (auth.uid() = author_id) Test Results: โโโ Anon SELECT: Only published posts (correct) โโโ Anon INSERT: โ Blocked (correct) โโโ Cross-user access: โ Blocked (correct) โโโ Filter bypass: โ Blocked (correct)
- orders RLS Enabled: โ YES Policies Found: 1 Status: ๐ P1 - PARTIAL ISSUE Policies: โโโ "Users see own orders" (SELECT) โโโ USING: (auth.uid() = user_id) Issue Found: โโโ No INSERT policy - users can't create orders via API โโโ No UPDATE policy - users can't modify their orders โโโ This may be intentional (orders via Edge Functions) Recommendation: Document if intentional, or add policies:
CREATE POLICY "Users insert own orders"
ON orders FOR INSERT
WITH CHECK (auth.uid() = user_id);
- comments RLS Enabled: โ YES Policies Found: 2 Status: ๐ P1 - BYPASS POSSIBLE Policies: โโโ "Anyone can read" (SELECT) โ โโโ USING: (true) โ Too permissive โโโ "Users comment on posts" (INSERT) โโโ WITH CHECK: (auth.uid() = user_id) Issue Found: โโโ SELECT policy allows reading all comments including user_id, enabling user correlation Recommendation:
-- Use a view to hide user_id
CREATE VIEW public.comments_public AS
SELECT id, post_id, content, created_at FROM comments;
- settings RLS Enabled: โ NO Status: ๐ด P0 - NO RLS PROTECTION Contains sensitive configuration! Immediate action required. โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Summary โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ RLS Disabled: 2 tables (users, settings) โ CRITICAL RLS Enabled: 6 tables โโโ Properly Configured: 3 โโโ Partial Issues: 2 โโโ Major Issues: 1 Bypass Tests: โโโ Unauthenticated access: 2 tables vulnerable โโโ Cross-user access: 0 tables vulnerable โโโ Filter bypass: 0 tables vulnerable โโโ Join exploitation: 1 table allows data leakage โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Context Output { "rls_audit" : { "timestamp" : "2025-01-31T10:45:00Z" , "tables_audited" : 8 , "summary" : { "rls_disabled" : 2 , "rls_enabled" : 6 , "properly_configured" : 3 , "partial_issues" : 2 , "major_issues" : 1 } , "findings" : [ { "table" : "users" , "rls_enabled" : false , "severity" : "P0" , "issue" : "No RLS protection" , "operations_exposed" : [ "SELECT" , "INSERT" , "UPDATE" , "DELETE" ] } , { "table" : "comments" , "rls_enabled" : true , "severity" : "P1" , "issue" : "Overly permissive SELECT policy" , "detail" : "user_id exposed enabling correlation" } ] } } Common RLS Patterns Good: User owns their data CREATE POLICY "Users own their data" ON user_data FOR ALL USING ( auth . uid ( ) = user_id ) WITH CHECK ( auth . uid ( ) = user_id ) ; Good: Public read, authenticated write -- Anyone can read CREATE POLICY "Public read" ON posts FOR SELECT USING ( published = true ) ; -- Only authors can write CREATE POLICY "Author write" ON posts FOR INSERT WITH CHECK ( auth . uid ( ) = author_id ) ; CREATE POLICY "Author update" ON posts FOR UPDATE USING ( auth . uid ( ) = author_id ) ; Bad: Using (true) -- โ Too permissive CREATE POLICY "Anyone" ON secrets FOR SELECT USING ( true ) ; Bad: Forgetting WITH CHECK -- โ Users can INSERT any user_id CREATE POLICY "Insert" ON posts FOR INSERT WITH CHECK ( true ) ; -- Should check user_id! RLS Bypass Documentation For each bypass found, the skill provides: Description of the vulnerability Proof of concept query Impact assessment Fix with SQL code Documentation link MANDATORY: Progressive Context File Updates โ ๏ธ This skill MUST update tracking files PROGRESSIVELY during execution, NOT just at the end. Critical Rule: Write As You Go DO NOT batch all writes at the end. Instead: Before testing each table โ Log the action to .sb-pentest-audit.log After each RLS finding โ Immediately update .sb-pentest-context.json After each test completes โ Log the result to .sb-pentest-audit.log This ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved. Required Actions (Progressive) Update .sb-pentest-context.json with results: { "rls_audit" : { "timestamp" : "..." , "tables_audited" : 8 , "summary" : { "rls_disabled" : 2 , ... } , "findings" : [ ... ] } } Log to .sb-pentest-audit.log : [TIMESTAMP] [supabase-audit-rls] [START] Auditing RLS policies [TIMESTAMP] [supabase-audit-rls] [FINDING] P0: users table has no RLS [TIMESTAMP] [supabase-audit-rls] [CONTEXT_UPDATED] .sb-pentest-context.json updated If files don't exist , create them before writing. FAILURE TO UPDATE CONTEXT FILES IS NOT ACCEPTABLE. MANDATORY: Evidence Collection ๐ Evidence Directory: .sb-pentest-evidence/03-api-audit/rls-tests/ Evidence Files to Create File Content rls-tests/[table]-anon.json Anonymous access test results rls-tests/[table]-auth.json Authenticated access test results rls-tests/cross-user-test.json Cross-user access attempts Evidence Format (RLS Bypass) { "evidence_id" : "RLS-001" , "timestamp" : "2025-01-31T10:25:00Z" , "category" : "api-audit" , "type" : "rls_test" , "severity" : "P0" , "table" : "users" , "rls_enabled" : false , "tests" : [ { "test_name" : "anon_select" , "description" : "Anonymous user SELECT access" , "request" : { "curl_command" : "curl -s '$URL/rest/v1/users?select=*&limit=5' -H 'apikey: $ANON_KEY'" } , "response" : { "status" : 200 , "rows_returned" : 5 , "total_accessible" : 1247 } , "result" : "VULNERABLE" , "impact" : "All user data accessible without authentication" } , { "test_name" : "anon_insert" , "description" : "Anonymous user INSERT access" , "request" : { "curl_command" : "curl -X POST '$URL/rest/v1/users' -H 'apikey: $ANON_KEY' -d '{...}'" } , "response" : { "status" : 201 } , "result" : "VULNERABLE" , "impact" : "Can create arbitrary user records" } ] , "remediation_sql" : "ALTER TABLE users ENABLE ROW LEVEL SECURITY;\nCREATE POLICY \"Users see own data\" ON users FOR SELECT USING (auth.uid() = id);" } Add to curl-commands.sh
=== RLS BYPASS TESTS ===
Test anon access to users table
curl -s " $SUPABASE_URL /rest/v1/users?select=*&limit=5" \ -H "apikey: $ANON_KEY " -H "Authorization: Bearer $ANON_KEY "
Test filter bypass
curl -s " $SUPABASE_URL /rest/v1/posts?or=(published.eq.true,published.eq.false)" \ -H "apikey: $ANON_KEY "