npx skills add ...
npx skills add yoanbernabeu/supabase-pentest-skills --skill supabase-audit-rls
Test Row Level Security (RLS) policies for common bypass vulnerabilities and misconfigurations.
npx skills add yoanbernabeu/supabase-pentest-skills --skill supabase-audit-rls
🔴 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:
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:
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:
Log to .sb-pentest-audit.log:
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 |
supabase-audit-tables-list — List tables firstsupabase-audit-tables-read — See actual data exposuresupabase-audit-rpc — RPC functions can bypass RLSsupabase-report — Full security reportGET /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_usersAudit RLS policies on my Supabase projectTest RLS on the users tableTest 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!