
supabase-audit-rpc
List and test exposed PostgreSQL RPC functions for security issues and potential RLS bypass.
- Installs
- 527
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-audit-rpc- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads supabase-audit-rpc automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
RPC Functions Audit
🔴 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 function tested- Log to
.sb-pentest-audit.logBEFORE and AFTER each function 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 discovers and tests PostgreSQL functions exposed via Supabase's RPC endpoint.
When to Use This Skill
- To discover exposed database functions
- To test if functions bypass RLS
- To check for SQL injection in function parameters
- As part of comprehensive API security testing
Prerequisites
- Supabase URL and anon key available
- Tables audit completed (recommended)
Understanding Supabase RPC
Supabase exposes PostgreSQL functions via:
POST https://[project].supabase.co/rest/v1/rpc/[function_name]
Functions can:
- ✅ Respect RLS (if using
auth.uid()and proper security) - ❌ Bypass RLS (if
SECURITY DEFINERwithout checks) - ❌ Execute arbitrary SQL (if poorly written)
Risk Levels for Functions
| Type | Risk | Description |
|---|---|---|
SECURITY INVOKER | Lower | Runs with caller's permissions |
SECURITY DEFINER | Higher | Runs with definer's permissions |
| Accepts text/json | Higher | Potential for injection |
| Returns setof | Higher | Can return multiple rows |
Usage
Basic RPC Audit
Audit RPC functions on my Supabase project
Test Specific Function
Test the get_user_data RPC function
Output Format
═══════════════════════════════════════════════════════════
RPC FUNCTIONS AUDIT
═══════════════════════════════════════════════════════════
Project: abc123def.supabase.co
Functions Found: 6
─────────────────────────────────────────────────────────
Function Inventory
─────────────────────────────────────────────────────────
1. get_user_profile(user_id uuid)
Security: INVOKER
Returns: json
Status: ✅ SAFE
Analysis:
├── Uses auth.uid() for authorization
├── Returns only caller's own profile
└── RLS is respected
2. search_posts(query text)
Security: INVOKER
Returns: setof posts
Status: ✅ SAFE
Analysis:
├── Parameterized query (no injection)
├── RLS filters results
└── Only returns published posts
3. get_all_users()
Security: DEFINER
Returns: setof users
Status: 🔴 P0 - RLS BYPASS
Analysis:
├── SECURITY DEFINER runs as owner
├── No auth.uid() check inside function
├── Returns ALL users regardless of caller
└── Bypasses RLS completely!
Test Result:
POST /rest/v1/rpc/get_all_users
→ Returns 1,247 user records with PII
Immediate Fix:
```sql
-- Add authorization check
CREATE OR REPLACE FUNCTION get_all_users()
RETURNS setof users
LANGUAGE sql
SECURITY INVOKER -- Change to INVOKER
AS $$
SELECT * FROM users
WHERE auth.uid() = id; -- Add RLS-like check
$$;
```
4. admin_delete_user(target_id uuid)
Security: DEFINER
Returns: void
Status: 🔴 P0 - CRITICAL VULNERABILITY
Analysis:
├── SECURITY DEFINER with delete capability
├── No role check (anon can call!)
├── Can delete any user
└── No audit trail
Test Result:
POST /rest/v1/rpc/admin_delete_user
Body: {"target_id": "any-uuid"}
→ Function accessible to anon!
Immediate Fix:
```sql
CREATE OR REPLACE FUNCTION admin_delete_user(target_id uuid)
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
-- Add role check
IF NOT (SELECT is_admin FROM profiles WHERE id = auth.uid()) THEN
RAISE EXCEPTION 'Unauthorized';
END IF;
DELETE FROM users WHERE id = target_id;
END;
$$;
-- Or better: restrict to authenticated only
REVOKE EXECUTE ON FUNCTION admin_delete_user FROM anon;
```
5. dynamic_query(table_name text, conditions text)
Security: DEFINER
Returns: json
Status: 🔴 P0 - SQL INJECTION
Analysis:
├── Accepts raw text parameters
├── Likely concatenates into query
├── SQL injection possible
Test Result:
POST /rest/v1/rpc/dynamic_query
Body: {"table_name": "users; DROP TABLE users;--", "conditions": "1=1"}
→ Injection vector confirmed!
Immediate Action:
→ DELETE THIS FUNCTION IMMEDIATELY
```sql
DROP FUNCTION IF EXISTS dynamic_query;
```
Never build queries from user input. Use parameterized queries.
6. calculate_total(order_id uuid)
Security: INVOKER
Returns: numeric
Status: ✅ SAFE
Analysis:
├── UUID parameter (type-safe)
├── SECURITY INVOKER respects RLS
└── Only accesses caller's orders
─────────────────────────────────────────────────────────
Summary
─────────────────────────────────────────────────────────
Total Functions: 6
Safe: 3
P0 Critical: 3
├── get_all_users (RLS bypass)
├── admin_delete_user (no auth check)
└── dynamic_query (SQL injection)
Priority Actions:
1. DELETE dynamic_query function immediately
2. Add auth checks to admin_delete_user
3. Fix get_all_users to respect RLS
═══════════════════════════════════════════════════════════
Injection Testing
The skill tests for SQL injection in text/varchar parameters:
Safe (Parameterized)
-- ✅ Safe: uses parameter placeholder
CREATE FUNCTION search_posts(query text)
RETURNS setof posts
AS $$
SELECT * FROM posts WHERE title ILIKE '%' || query || '%';
$$ LANGUAGE sql;
Vulnerable (Concatenation)
-- ❌ Vulnerable: dynamic SQL execution
CREATE FUNCTION dynamic_query(tbl text, cond text)
RETURNS json
AS $$
DECLARE result json;
BEGIN
EXECUTE format('SELECT json_agg(t) FROM %I t WHERE %s', tbl, cond)
INTO result;
RETURN result;
END;
$$ LANGUAGE plpgsql;
Context Output
{
"rpc_audit": {
"timestamp": "2025-01-31T11:00:00Z",
"functions_found": 6,
"summary": {
"safe": 3,
"p0_critical": 3,
"p1_high": 0
},
"findings": [
{
"function": "get_all_users",
"severity": "P0",
"issue": "RLS bypass via SECURITY DEFINER",
"impact": "All user data accessible",
"remediation": "Change to SECURITY INVOKER or add auth checks"
},
{
"function": "dynamic_query",
"severity": "P0",
"issue": "SQL injection vulnerability",
"impact": "Arbitrary SQL execution possible",
"remediation": "Delete function, use parameterized queries"
}
]
}
}
Best Practices for RPC Functions
1. Prefer SECURITY INVOKER
CREATE FUNCTION my_function()
RETURNS ...
SECURITY INVOKER -- Respects RLS
AS $$ ... $$;
2. Always Check auth.uid()
CREATE FUNCTION get_my_data()
RETURNS json
AS $$
SELECT json_agg(d) FROM data d
WHERE d.user_id = auth.uid(); -- Always filter by caller
$$ LANGUAGE sql SECURITY INVOKER;
3. Use REVOKE for Sensitive Functions
-- Remove anon access
REVOKE EXECUTE ON FUNCTION admin_function FROM anon;
-- Only authenticated users
GRANT EXECUTE ON FUNCTION admin_function TO authenticated;
4. Avoid Text Parameters for Dynamic Queries
-- ❌ Bad
CREATE FUNCTION query(tbl text) ...
-- ✅ Good: use specific functions per table
CREATE FUNCTION get_users() ...
CREATE FUNCTION get_posts() ...
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:
- Before testing each function → Log the action to
.sb-pentest-audit.log - After each function analyzed → Immediately update
.sb-pentest-context.json - After each vulnerability found → Log the finding immediately
This ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved.
Required Actions (Progressive)
-
Update
.sb-pentest-context.jsonwith results:{ "rpc_audit": { "timestamp": "...", "functions_found": 6, "summary": { "safe": 3, "p0_critical": 3 }, "findings": [ ... ] } } -
Log to
.sb-pentest-audit.log:[TIMESTAMP] [supabase-audit-rpc] [START] Auditing RPC functions [TIMESTAMP] [supabase-audit-rpc] [FINDING] P0: dynamic_query has SQL injection [TIMESTAMP] [supabase-audit-rpc] [CONTEXT_UPDATED] .sb-pentest-context.json updated -
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/03-api-audit/rpc-tests/
Evidence Files to Create
| File | Content |
|---|---|
function-list.json | All discovered RPC functions |
vulnerable-functions/[name].json | Details for each vulnerable function |
Evidence Format (Vulnerable Function)
{
"evidence_id": "RPC-001",
"timestamp": "2025-01-31T10:30:00Z",
"category": "api-audit",
"type": "rpc_vulnerability",
"severity": "P0",
"function": "get_all_users",
"analysis": {
"security_definer": true,
"auth_check": false,
"rls_bypass": true
},
"test": {
"request": {
"method": "POST",
"url": "https://abc123def.supabase.co/rest/v1/rpc/get_all_users",
"curl_command": "curl -X POST '$URL/rest/v1/rpc/get_all_users' -H 'apikey: $ANON_KEY' -H 'Content-Type: application/json'"
},
"response": {
"status": 200,
"rows_returned": 1247,
"sample_data": "[REDACTED - contains user PII]"
}
},
"impact": "Bypasses RLS, returns all 1,247 user records",
"remediation": "Change to SECURITY INVOKER or add auth.uid() check"
}
Add to curl-commands.sh
# === RPC FUNCTION TESTS ===
# Test get_all_users function (P0 if accessible)
curl -X POST "$SUPABASE_URL/rest/v1/rpc/get_all_users" \
-H "apikey: $ANON_KEY" \
-H "Content-Type: application/json"
# Test admin_delete_user function
curl -X POST "$SUPABASE_URL/rest/v1/rpc/admin_delete_user" \
-H "apikey: $ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"target_id": "test-uuid"}'
Related Skills
supabase-audit-tables-list— List exposed tablessupabase-audit-rls— Test RLS policiessupabase-audit-auth-users— User enumeration tests
FAQ
What does the supabase-audit-rpc skill do?
List and test exposed PostgreSQL RPC functions for security issues and potential RLS bypass.
How do I install supabase-audit-rpc?
Run npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-audit-rpc in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/supabase-audit-rpc/ (personal) or .claude/skills/supabase-audit-rpc/ (project).
Which AI agents can use supabase-audit-rpc?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is supabase-audit-rpc safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains supabase-audit-rpc?
It is published by yoanbernabeu in yoanbernabeu/supabase-pentest-skills. It has 527 recorded installs.
More from yoanbernabeu/supabase-pentest-skills
supabase-audit-rls
yoanbernabeu/supabase-pentest-skills
Test Row Level Security (RLS) policies for common bypass vulnerabilities and misconfigurations.
supabase-audit-functions
yoanbernabeu/supabase-pentest-skills
Discover and test Supabase Edge Functions for security vulnerabilities and misconfigurations.
supabase-pentest
yoanbernabeu/supabase-pentest-skills
Orchestrate a complete Supabase security audit with guided step-by-step execution and ownership confirmation.
supabase-audit-auth-config
yoanbernabeu/supabase-pentest-skills
Analyze Supabase authentication configuration for security weaknesses and misconfigurations.
supabase-audit-realtime
yoanbernabeu/supabase-pentest-skills
Test Supabase Realtime WebSocket channels for unauthorized subscriptions and data exposure.
supabase-help
yoanbernabeu/supabase-pentest-skills
Quick reference for all Supabase security audit skills with usage examples and command overview.
Popular data & databases skills
supabase-postgres-best-practices
supabase/agent-skills
Postgres best practices maintained by Supabase, for Postgres running anywhere. Load this skill BEFORE writing or changing anything that lives in a Postgres database: creating or altering tables and columns (including choosing column types), schema design, migrations and declarative schema files, RLS policies and the tests that verify them, indexes, triggers, database functions, queues and scheduled jobs (pgcron, pgmq), vector/semantic search (pgvector), and restoring dumps (pgrestore) or importing data. Also load it when diagnosing slow queries, high CPU, timeouts, EXPLAIN plans, connection exhaustion, locking, bloat, or rows visible to the wrong user or tenant. This is not just a performance guide — schema, migration, security, and SQL authoring tasks need these rules too, even for a one-column change or a single query.
prisma-client-api
prisma/skills
Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on "prisma query", "findMany", "create", "update", "delete", "$transaction".
prisma-database-setup
prisma/skills
Deprecated compatibility name for prisma-orm-setup. Use only when a user or existing integration explicitly invokes prisma-database-setup; otherwise select prisma-orm-setup.
prisma-cli
prisma/skills
Prisma ORM 7 CLI reference for existing Prisma 7 applications or explicitly selected Prisma 7 workflows. Covers prisma init, generate, migrate, db, dev, studio, and mcp. For setup, connection repair, or Prisma 8, use prisma-orm-setup; not the Prisma Platform CLI.
prisma-postgres
prisma/skills
Deprecated compatibility name for prisma-postgres-setup. Use only when a user or existing integration explicitly invokes prisma-postgres; otherwise select prisma-postgres-setup.
prisma-driver-adapter-implementation
prisma/skills
Required reference for Prisma ORM 7 SQL driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter, Transaction, savepoint, result mapping, or DriverAdapterError behavior. Covers current transaction lifecycle, optional savepoint hooks, original database-error preservation, and verification.