Claude
Skills
← Back

supabase-audit-tables-list

List all tables exposed via the Supabase PostgREST API to identify the attack surface.

Backend & APIs

What this skill does


# List Exposed Tables

> šŸ”“ **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 discovery**
> - Log to `.sb-pentest-audit.log` **BEFORE 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.

## When to Use This Skill

- To understand the API attack surface
- Before testing RLS policies
- To inventory exposed data models
- As part of a comprehensive security audit

## Prerequisites

- Supabase URL extracted (auto-invokes if needed)
- Anon key extracted (auto-invokes if needed)

## How It Works

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]
```

## What Gets Exposed

By default, Supabase exposes tables in the `public` schema. Tables are exposed when:

1. They exist in an exposed schema (default: `public`)
2. No explicit `REVOKE` has been done
3. PostgREST can see them

## Usage

### Basic Table List

```
List tables exposed on my Supabase project
```

### With Schema Information

```
List all exposed tables with column details
```

## Output Format

```
═══════════════════════════════════════════════════════════
 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

═══════════════════════════════════════════════════════════
```

## Risk Classification

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 |

## Context Output

```json
{
  "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"]
    }
  }
}
```

## Hidden Tables

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.
═══════════════════════════════════════════════════════════
```

## Schema Analysis

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
```

## Common Issues

āŒ **Problem:** No tables found
āœ… **Solution:**
- Check if anon key is valid
- Verify project URL is correct
- The API may be disabled in project settings

āŒ **Problem:** Too many tables listed
āœ… **Solution:** This may indicate overly permissive schema exposure. Consider:
```sql
-- 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.

## Recommendations by Table Type

### User Tables

```sql
-- 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);
```

### Order/Payment Tables

```sql
-- 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
```

### Secret Tables

```sql
-- 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;
```

## 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:

1. **Before starting any action** → Log the action to `.sb-pentest-audit.log`
2. **After each table discovered** → Immediately update `.sb-pentest-context.json`
3. **After each significant step** → Log completion 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)

1. **Update `.sb-pentest-context.json`** with results:
   ```json
   {
     "tables": {
       "count": 8,
       "list": [ ... ],
       "by_risk": { "high": [], "medium": [], "low": [] }
     }
   }
   ```

2. **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

Related in Backend & APIs