List all tables exposed via the Supabase PostgREST API to identify the attack surface.
š“ 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 discovery- Log to
.sb-pentest-audit.logBEFORE and AFTER each action- 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 discovers all database tables exposed through the Supabase PostgREST API.
Supabase exposes tables via PostgREST at:
https://[project-ref].supabase.co/rest/v1/
The skill uses the OpenAPI schema endpoint to enumerate tables:
https://[project-ref].supabase.co/rest/v1/?apikey=[anon-key]
By default, Supabase exposes tables in the public schema. Tables are exposed when:
public)REVOKE has been doneList tables exposed on my Supabase project
List all exposed tables with column details
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
EXPOSED TABLES
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Project: abc123def.supabase.co
Schema: public
Tables Found: 8
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Table Inventory
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
1. users
āāā Columns: id, email, name, avatar_url, created_at
āāā Primary Key: id (uuid)
āāā RLS Status: Unknown (test with supabase-audit-rls)
āāā Risk: ā ļø Contains user PII
2. profiles
āāā Columns: id, user_id, bio, website, social_links
āāā Primary Key: id (uuid)
āāā Foreign Key: user_id ā auth.users
āāā Risk: ā ļø Contains user PII
3. posts
āāā Columns: id, author_id, title, content, published, created_at
āāā Primary Key: id (uuid)
āāā Risk: ā¹ļø Content data
4. comments
āāā Columns: id, post_id, user_id, content, created_at
āāā Primary Key: id (uuid)
āāā Risk: ā¹ļø Content data
5. orders
āāā Columns: id, user_id, total, status, items, created_at
āāā Primary Key: id (uuid)
āāā Risk: š“ Contains financial/transaction data
6. products
āāā Columns: id, name, description, price, stock, image_url
āāā Primary Key: id (uuid)
āāā Risk: ā¹ļø Public catalog data
7. settings
āāā Columns: id, key, value, updated_at
āāā Primary Key: id (uuid)
āāā Risk: ā ļø May contain sensitive configuration
8. api_keys
āāā Columns: id, user_id, key_hash, name, last_used
āāā Primary Key: id (uuid)
āāā Risk: š“ Contains secrets
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Summary
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Total Tables: 8
High Risk: 2 (orders, api_keys)
Medium Risk: 3 (users, profiles, settings)
Low Risk: 3 (posts, comments, products)
Next Steps:
āāā Run supabase-audit-tables-read to test actual data access
āāā Run supabase-audit-rls to verify RLS policies
āāā Review high-risk tables first
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Tables are classified by likely content:
| Risk | Table Patterns | Examples |
|---|---|---|
| š“ High | Financial, secrets, auth | orders, payments, api_keys, secrets |
| ā ļø Medium | User PII, config | users, profiles, settings, preferences |
| ā¹ļø Low | Public content | posts, products, categories, tags |
{
"tables": {
"count": 8,
"list": [
{
"name": "users",
"schema": "public",
"columns": ["id", "email", "name", "avatar_url", "created_at"],
"primary_key": "id",
"risk_level": "medium",
"risk_reason": "Contains user PII"
},
{
"name": "orders",
"schema": "public",
"columns": ["id", "user_id", "total", "status", "items", "created_at"],
"primary_key": "id",
"risk_level": "high",
"risk_reason": "Contains financial data"
}
],
"by_risk": {
"high": ["orders", "api_keys"],
"medium": ["users", "profiles", "settings"],
"low": ["posts", "comments", "products"]
}
}
}
Some tables may not appear in the OpenAPI schema:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ADDITIONAL DISCOVERY
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Common Tables Not in Schema (testing existence):
āāā _prisma_migrations: ā Not found
āāā schema_migrations: ā Not found
āāā audit_log: ā
EXISTS but not in OpenAPI
āāā internal_config: ā Not found
Note: 'audit_log' exists but may have restricted access.
Test with supabase-audit-tables-read.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
The skill also checks for non-public schemas:
Schema Exposure Check:
āāā public: ā
Exposed (8 tables)
āāā auth: ā Not directly exposed (expected)
āāā storage: ā Not directly exposed (expected)
āāā extensions: ā Not exposed (good)
āāā custom_schema: ā ļø Exposed (3 tables) - Review if intentional
ā Problem: No tables found ā Solution:
ā Problem: Too many tables listed ā Solution: This may indicate overly permissive schema exposure. Consider:
-- Restrict exposed schemas
ALTER ROLE anon SET search_path TO public;
ā Problem: Sensitive tables exposed ā Solution: Either remove from public schema or implement strict RLS.
-- Ensure RLS is enabled
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
-- Users can only see their own data
CREATE POLICY "Users see own data" ON users
FOR SELECT USING (auth.uid() = id);
-- Strict RLS for financial data
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users see own orders" ON orders
FOR SELECT USING (auth.uid() = user_id);
-- No public access even for admins via API
-- Use Edge Functions for admin operations
-- Consider not exposing at all
REVOKE ALL ON TABLE api_keys FROM anon, authenticated;
-- Or use views that hide sensitive columns
CREATE VIEW public.api_keys_safe AS
SELECT id, name, last_used FROM api_keys;
ā ļø 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:
{
"tables": {
"count": 8,
"list": [ ... ],
"by_risk": { "high": [], "medium": [], "low": [] }
}
}
Log to .sb-pentest-audit.log:
[TIMESTAMP] [supabase-audit-tables-list] [START] Listing exposed tables
[TIMESTAMP] [supabase-audit-tables-list] [SUCCESS] Found 8 tables
[TIMESTAMP] [supabase-audit-tables-list] [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/tables/
| File | Content |
|---|---|
tables-list.json |
Complete list of exposed tables |
tables-metadata.json |
Column details and types per table |
openapi-schema.json |
Raw OpenAPI/PostgREST schema |
{
"evidence_id": "API-TBL-001",
"timestamp": "2025-01-31T10:15:00Z",
"category": "api-audit",
"type": "table_enumeration",
"request": {
"method": "GET",
"url": "https://abc123def.supabase.co/rest/v1/",
"headers": {
"apikey": "[REDACTED]"
},
"curl_command": "curl -s 'https://abc123def.supabase.co/rest/v1/' -H 'apikey: $ANON_KEY'"
},
"tables_found": [
{
"name": "users",
"schema": "public",
"columns": ["id", "email", "name", "created_at"],
"primary_key": "id",
"risk_level": "high",
"risk_reason": "Contains PII"
},
{
"name": "orders",
"schema": "public",
"columns": ["id", "user_id", "total", "status"],
"primary_key": "id",
"risk_level": "high",
"risk_reason": "Financial data"
}
],
"summary": {
"total_tables": 8,
"high_risk": 2,
"medium_risk": 3,
"low_risk": 3
}
}
# === TABLE ENUMERATION ===
# List all exposed tables via OpenAPI schema
curl -s "$SUPABASE_URL/rest/v1/" -H "apikey: $ANON_KEY"
supabase-audit-tables-read ā Test actual data accesssupabase-audit-rls ā Verify RLS policiessupabase-audit-rpc ā Check exposed functions