home / skills / yoanbernabeu / supabase-pentest-skills / supabase-extract-db-string

supabase-extract-db-string skill

/skills/extraction/supabase-extract-db-string

This skill detects exposed PostgreSQL connection strings in client-side code and guides immediate remediation to prevent direct database access.

npx playbooks add skill yoanbernabeu/supabase-pentest-skills --skill supabase-extract-db-string

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
12.8 KB
---
name: supabase-extract-db-string
description: CRITICAL - Detect exposed PostgreSQL database connection strings in client-side code. Direct DB access is a P0 issue.
---

# Database Connection String Detection

> šŸ”“ **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 detects if PostgreSQL database connection strings are accidentally exposed in client-side code.

## When to Use This Skill

- As part of every security audit
- When reviewing code before production
- When Supabase database access is suspected

## Prerequisites

- Target application accessible
- Supabase detection completed (auto-invokes if needed)

## Why This Is Critical

Exposed database connection strings allow:

| Impact | Description |
|--------|-------------|
| šŸ”“ Direct DB Access | Bypass API, connect directly to PostgreSQL |
| šŸ”“ Full Data Access | Read/write all data without RLS |
| šŸ”“ Schema Access | View and modify database structure |
| šŸ”“ User Enumeration | Access auth.users table directly |

**This is a P0 (Critical) finding requiring immediate action.**

## Connection String Patterns

### Supabase Database URL

```
postgresql://postgres:[password]@db.[project-ref].supabase.co:5432/postgres
```

### Connection String Components

| Component | Example | Sensitivity |
|-----------|---------|-------------|
| Host | `db.abc123.supabase.co` | Medium |
| Port | `5432` | Low |
| Database | `postgres` | Low |
| Username | `postgres` | Medium |
| Password | `[your-password]` | šŸ”“ Critical |

### Pooler Connection (Supavisor)

```
postgresql://postgres.[project-ref]:[password]@aws-0-us-east-1.pooler.supabase.com:6543/postgres
```

## Detection Patterns

### 1. Full Connection Strings

```javascript
// āŒ CRITICAL - Full connection string
const dbUrl = 'postgresql://postgres:[email protected]:5432/postgres'
```

### 2. Environment Variable Leaks

```javascript
// āŒ Exposed in client bundle
process.env.DATABASE_URL
process.env.POSTGRES_URL
process.env.SUPABASE_DB_URL
```

### 3. Partial Exposure

```javascript
// āš ļø Password exposed separately
const DB_PASSWORD = 'MySecretPass123'
const DB_HOST = 'db.abc123.supabase.co'
```

### 4. ORM Configuration

```javascript
// āŒ Database config in client code
const prisma = new PrismaClient({
  datasources: {
    db: {
      url: 'postgresql://postgres:[email protected]:5432/postgres'
    }
  }
})
```

## Usage

### Basic Check

```
Check for database connection strings on https://myapp.example.com
```

### Deep Scan

```
Deep scan for DB credentials on https://myapp.example.com
```

## Output Format

### No Connection String Found (Good)

```
═══════════════════════════════════════════════════════════
 DATABASE CONNECTION STRING CHECK
═══════════════════════════════════════════════════════════

 Status: āœ… No database connection strings detected

 Scanned:
 ā”œā”€ā”€ JavaScript bundles: 5 files analyzed
 ā”œā”€ā”€ PostgreSQL patterns: None found
 ā”œā”€ā”€ Connection strings: None found
 └── Password patterns: None found

 Result: PASS - No direct database credentials exposed
═══════════════════════════════════════════════════════════
```

### Connection String FOUND (Critical)

```
═══════════════════════════════════════════════════════════
 šŸ”“ CRITICAL: DATABASE CONNECTION STRING EXPOSED
═══════════════════════════════════════════════════════════

 Severity: P0 - CRITICAL
 Status: āŒ PostgreSQL connection string found in client code!

 āš ļø  IMMEDIATE ACTION REQUIRED āš ļø

 Connection String:
 postgresql://postgres:MySecr***@db.abc123def.supabase.co:5432/postgres
 (Password partially redacted in display, full value in context file)

 Parsed Components:
 ā”œā”€ā”€ Host: db.abc123def.supabase.co
 ā”œā”€ā”€ Port: 5432
 ā”œā”€ā”€ Database: postgres
 ā”œā”€ā”€ Username: postgres
 └── Password: [EXPOSED] ← CRITICAL

 Location:
 └── /static/js/api.chunk.js (line 234)
     const DATABASE_URL = 'postgresql://postgres:...'

 Impact Assessment:
 ā”œā”€ā”€ šŸ”“ Direct PostgreSQL access possible
 ā”œā”€ā”€ šŸ”“ All RLS policies bypassed
 ā”œā”€ā”€ šŸ”“ Can access auth.users table
 ā”œā”€ā”€ šŸ”“ Can modify database schema
 └── šŸ”“ Full data exfiltration possible

 ═══════════════════════════════════════════════════════════
 IMMEDIATE REMEDIATION STEPS
 ═══════════════════════════════════════════════════════════

 1. CHANGE DATABASE PASSWORD NOW
    → Supabase Dashboard > Settings > Database > Reset database password

 2. REMOVE FROM CLIENT CODE
    → Delete connection string from source code
    → Ensure DATABASE_URL is not in NEXT_PUBLIC_* or VITE_* env vars
    → Redeploy application

 3. AUDIT FOR ABUSE
    → Check Supabase logs for direct PostgreSQL connections
    → Review for unauthorized data access or modifications

 4. USE PROPER ARCHITECTURE
    → Client should ONLY use Supabase client library (REST API)
    → Direct DB access should ONLY be from:
      - Edge Functions
      - Server-side code
      - Migration tools

 Documentation:
 → https://supabase.com/docs/guides/database/connecting-to-postgres
 → https://supabase.com/docs/guides/functions

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

## Context Output

```json
{
  "findings": [
    {
      "id": "DB_CONNECTION_STRING_EXPOSED",
      "severity": "P0",
      "title": "PostgreSQL Connection String Exposed",
      "description": "Database connection string with password found in client-side code",
      "location": {
        "file": "/static/js/api.chunk.js",
        "line": 234
      },
      "evidence": {
        "host": "db.abc123def.supabase.co",
        "port": 5432,
        "database": "postgres",
        "username": "postgres",
        "password_exposed": true
      },
      "remediation": {
        "immediate": "Reset database password in Supabase Dashboard",
        "long_term": "Move DB operations to Edge Functions",
        "docs": "https://supabase.com/docs/guides/database/connecting-to-postgres"
      }
    }
  ],
  "supabase": {
    "db_string_exposed": true,
    "db_host": "db.abc123def.supabase.co"
  }
}
```

## Partial Exposure

Even partial exposure is concerning:

```
═══════════════════════════════════════════════════════════
 āš ļø PARTIAL DATABASE CREDENTIALS FOUND
═══════════════════════════════════════════════════════════

 Severity: P1 - High

 Found:
 ā”œā”€ā”€ Database host: db.abc123def.supabase.co (line 45)
 ā”œā”€ā”€ Database password: [16 char string] (line 89)
 └── Could potentially be combined for access

 Recommendation:
 → Rotate database password as precaution
 → Remove all DB-related values from client code
═══════════════════════════════════════════════════════════
```

## Common Causes

| Cause | Solution |
|-------|----------|
| Wrong env prefix | Never use `NEXT_PUBLIC_DATABASE_URL` |
| SSR code in client | Ensure server-only code stays server-side |
| Bundler misconfiguration | Review webpack/vite config for env exposure |
| Copy-paste error | Double-check what you're committing |

## Architecture Guidance

### Wrong (Direct DB in Client)

```javascript
// āŒ NEVER in client code
import { Pool } from 'pg'
const pool = new Pool({
  connectionString: process.env.DATABASE_URL  // āŒ
})
```

### Correct (API or Edge Function)

```javascript
// āœ… Client uses Supabase client
const { data } = await supabase
  .from('products')
  .select('*')

// OR call an Edge Function for complex queries
const { data } = await supabase.functions.invoke('complex-query')
```

### Edge Function (Server-Side)

```typescript
// supabase/functions/complex-query/index.ts
import { createClient } from '@supabase/supabase-js'

Deno.serve(async (req) => {
  // āœ… Direct DB access only on server
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL'),
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
  )

  // Complex query that can't be done via REST
  const { data } = await supabase.rpc('complex_function')
  return new Response(JSON.stringify(data))
})
```

## 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 discovery** → 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 findings:
   ```json
   {
     "supabase": {
       "db_string_exposed": true/false,
       "db_host": "db.[ref].supabase.co"
     },
     "findings": [
       {
         "id": "DB_CONNECTION_STRING_EXPOSED",
         "severity": "P0",
         ...
       }
     ]
   }
   ```

2. **Log to `.sb-pentest-audit.log`**:
   ```
   [TIMESTAMP] [supabase-extract-db-string] [START] Checking for DB connection strings
   [TIMESTAMP] [supabase-extract-db-string] [CRITICAL] Connection string EXPOSED
   [TIMESTAMP] [supabase-extract-db-string] [CONTEXT_UPDATED] .sb-pentest-context.json updated
   ```

3. **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/02-extraction/db-string-exposure/`

### Evidence Files to Create (if DB string found)

| File | Content |
|------|---------|
| `db-string-exposure/connection-details.json` | Parsed connection string (password redacted) |
| `db-string-exposure/location.txt` | File path and line number |

### Evidence Format (P0 Finding)

```json
{
  "evidence_id": "EXT-DB-001",
  "timestamp": "2025-01-31T10:12:00Z",
  "category": "extraction",
  "type": "db_connection_string",
  "severity": "P0",
  "finding_id": "P0-002",

  "connection_string": {
    "pattern": "postgresql://postgres:[REDACTED]@db.abc123def.supabase.co:5432/postgres",
    "host": "db.abc123def.supabase.co",
    "port": 5432,
    "database": "postgres",
    "username": "postgres",
    "password_exposed": true,
    "password_length": 24
  },

  "location": {
    "file": "/static/js/api.chunk.js",
    "line": 234,
    "context": "const DATABASE_URL = 'postgresql://postgres:...' // [REDACTED]"
  },

  "impact": {
    "direct_db_access": true,
    "rls_bypass": true,
    "schema_access": true,
    "auth_users_access": true
  },

  "remediation": {
    "immediate": "Reset database password in Supabase Dashboard",
    "remove_from_code": "Delete DATABASE_URL from client code",
    "verify_env_vars": "Ensure not using NEXT_PUBLIC_DATABASE_URL or similar"
  }
}
```

### Add to timeline.md (P0)

```markdown
## [TIMESTAMP] - šŸ”“ P0 CRITICAL: Database Connection String Exposed
- PostgreSQL connection string with password found in client code
- Location: [file]:[line]
- Impact: Direct database access, full RLS bypass
- Evidence: `02-extraction/db-string-exposure/`
- **IMMEDIATE PASSWORD ROTATION REQUIRED**
```

## Related Skills

- `supabase-extract-service-key` — Check for service key exposure
- `supabase-audit-tables-read` — Test data access via API
- `supabase-report` — Generate comprehensive report

Overview

This skill detects exposed PostgreSQL connection strings in client-side code and flags them as immediate, critical findings. It identifies full connection URLs, partial leaks (host or password), and environment-variable exposures that allow direct DB access. The output includes parsed evidence, remediation steps, and progressive context updates for reliable incident tracking.

How this skill works

The skill scans client bundles, static assets, and environment variable references for known PostgreSQL and Supabase connection string patterns. It extracts and parses full URLs and partial components, records file locations and line numbers, and generates redacted evidence for reporting. On each discovery and after each significant action, it updates context and audit files so findings are preserved even if execution is interrupted.

When to use it

  • During every security audit of a Supabase app
  • Before production deployments or public releases
  • When Supabase detection indicates potential misconfiguration
  • If client bundles or frontend code include server-side env vars
  • When investigating suspected direct DB access or abnormal activity

Best practices

  • Treat any exposed DB connection string as P0 — rotate the password immediately
  • Never expose DATABASE_URL in client-side env vars (avoid NEXT_PUBLIC_* / VITE_*)
  • Keep direct DB access limited to server-side code, Edge Functions, or migration tools
  • Write progressive logs and context updates after each discovery to preserve evidence
  • Store evidence with redacted passwords and strict access controls

Example use cases

  • Scan a deployed frontend URL for hardcoded postgresql:// URLs and report exact file and line
  • Detect partial leaks where host and password appear separately and flag combined risk
  • Find ORM configs or Prisma datasources accidentally bundled into client builds
  • Validate that environment variables exposed to the client do not contain DB credentials
  • Produce P0 evidence files and remediation steps for rapid incident response

FAQ

What makes this finding P0 (critical)?

A full connection string in client code provides direct PostgreSQL access, bypasses RLS, and can lead to full data exfiltration and schema changes — immediate action is required.

If only the host is found, is it still serious?

Yes. Partial exposures (host or password) raise high risk because they can be combined with other leaks; rotate credentials and remove values from client code as a precaution.