with one click
supabase-audit-rls
// Test Row Level Security (RLS) policies for common bypass vulnerabilities and misconfigurations.
// Test Row Level Security (RLS) policies for common bypass vulnerabilities and misconfigurations.
Orchestrate a complete Supabase security audit with guided step-by-step execution and ownership confirmation.
List and test exposed PostgreSQL RPC functions for security issues and potential RLS bypass.
List all tables exposed via the Supabase PostgREST API to identify the attack surface.
Attempt to read data from exposed tables to verify actual data exposure and RLS effectiveness.
Analyze Supabase authentication configuration for security weaknesses and misconfigurations.
Test if user signup is open and identify potential abuse vectors in the registration process.
| name | supabase-audit-rls |
| description | Test Row Level Security (RLS) policies for common bypass vulnerabilities and misconfigurations. |
š“ CRITICAL: PROGRESSIVE FILE UPDATES REQUIRED
You MUST write to context files AS YOU GO, not just at the end.
- Write to
.sb-pentest-context.jsonIMMEDIATELY after each finding- Log to
.sb-pentest-audit.logBEFORE 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.
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.
| 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 |
The skill tests these common bypass scenarios:
GET /rest/v1/users?select=*
# No Authorization header or with anon key only
# 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]
# Try to bypass filters with OR conditions
GET /rest/v1/posts?or=(published.eq.true,published.eq.false)
# Try to access data through related tables
GET /rest/v1/comments?select=*,posts(*)
# Check if RPC functions bypass RLS
POST /rest/v1/rpc/get_all_users
Audit RLS policies on my Supabase project
Test RLS on the users table
Test RLS policies using this user token: eyJ...
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
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:
```sql
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users see own data"
ON users FOR ALL
USING (auth.uid() = id);
```
2. 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)
3. 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:
```sql
CREATE POLICY "Users insert own orders"
ON orders FOR INSERT
WITH CHECK (auth.uid() = user_id);
```
4. 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:
```sql
-- Use a view to hide user_id
CREATE VIEW public.comments_public AS
SELECT id, post_id, content, created_at FROM comments;
```
5. 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
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
{
"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"
}
]
}
}
CREATE POLICY "Users own their data"
ON user_data FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- 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);
-- ā Too permissive
CREATE POLICY "Anyone" ON secrets
FOR SELECT USING (true);
-- ā Users can INSERT any user_id
CREATE POLICY "Insert" ON posts
FOR INSERT WITH CHECK (true); -- Should check user_id!
For each bypass found, the skill provides:
ā ļø This skill MUST update tracking files PROGRESSIVELY during execution, NOT just at the end.
DO NOT batch all writes at the end. Instead:
.sb-pentest-audit.log.sb-pentest-context.json.sb-pentest-audit.logThis ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved.
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.
š Evidence Directory: .sb-pentest-evidence/03-api-audit/rls-tests/
| 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_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);"
}
# === 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"
supabase-audit-tables-list ā List tables firstsupabase-audit-tables-read ā See actual data exposuresupabase-audit-rpc ā RPC functions can bypass RLSsupabase-report ā Full security report