Supabase security
Skills pessoais
npx -y skills add prof-ramos/skills --skill supabase-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Audits Supabase security on authorized bug bounty and pentest targets, focusing on misconfigurations common in vibe-coded and production apps: missing/broken RLS, USING(true)/WITH CHECK(true), JWT user_metadata privilege escalation, anon write policies, SECURITY DEFINER RPCs, storage buckets, exposed service_role keys, and PostgREST surface. Guides running the bundled SQL audit scripts, interpreting red flags, validating client-side exposure, and writing severity-ranked findings with PoC steps and remediations. Use when the user runs /supabase-security, or asks to audit Supabase, check RLS, review Supabase policies, hunt Supabase misconfigs, test anon key access, or secure a Supabase backend on a bug bounty target. Requires written authorization before any active testing.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
9.9 KB, ~2.3k tokens by cl100k_base, as published. Nobody here has run it
Supabase Security Audit (Bug Bounty)
Full security verification of a target's Supabase stack. Vibe-coded and many production apps share the same failure modes: RLS off, open policies, JWT metadata for roles, open Storage, and SECURITY DEFINER RPCs.
Authorization gate Use only on targets with written authorization (bug bounty program, pentest SOW, or owned project). Confirm in-scope assets (domains, project refs, APIs) before any active test. Prefer non-destructive checks. Do not mass-exfiltrate PII; minimize evidence to PoC rows/fields. If a
service_rolekey is found, treat as Critical secret exposure — do not abuse it beyond program-allowed proof unless rules explicitly allow.
When to run
- User runs
/supabase-security - Target uses Supabase (
.supabase.co,createClient,NEXT_PUBLIC_SUPABASE_*, PostgREST/rest/v1) - Need RLS / anon-key / Storage / RPC review for bug bounty or hardening
Inputs to collect
Ask only for what is missing:
| Input | Why |
|---|---|
| Target URL / app URL | JS recon, key harvesting |
| Program scope / auth proof | Legal boundary |
Supabase project URL (https://<ref>.supabase.co) | API surface |
anon public key (from frontend) | Authenticated-as-anon tests |
Optional: SQL Editor / psql access | Full catalog audit (when authorized & available) |
| Optional: two test accounts | IDOR / horizontal privilege checks |
Pipeline
0 Auth gate → 1 Recon → 2 Client surface → 3 Catalog SQL (if possible)
→ 4 Abuse validation → 5 Severity + report
Do not skip 0–2. Phase 3 requires DB access (Dashboard SQL Editor or psql); if unavailable, deepen 2 and 4 from the outside.
Phase 0 — Authorization
- Confirm target is in scope and testing is authorized.
- Note program rules on automated scanning, account creation, and credential use.
- If authorization is unclear, stop and ask the user.
Phase 1 — Recon
Map how the app talks to Supabase.
- Frontend / mobile bundle
- Search for:
supabase.co,SUPABASE_URL,SUPABASE_ANON_KEY,service_role,eyJJWTs,createClient(. - Extract project ref, anon key, any leaked service_role or DB URLs.
- Search for:
- OpenAPI / PostgREST
GET {SUPABASE_URL}/rest/v1/with headers:apikey: <anon>Authorization: Bearer <anon>
- Note exposed tables, views, RPC names if schema is open or errors leak names.
- Auth methods in UI: email/password, magic link, OAuth, phone, anonymous sign-in.
- Storage / Realtime / Edge usage in network tab or source.
- Produce a short attack-surface note: project ref, keys found (redact in reports as needed), tables/RPCs suspected, auth model.
Phase 2 — Client-side surface (anon / user JWT)
All tests use only public credentials (anon key + attacker-controlled accounts) unless rules say otherwise.
2A. REST data access
For each suspected table T:
GET /rest/v1/T?select=*&limit=5
apikey: <anon>
Authorization: Bearer <anon_or_user_jwt>
Check:
| Check | Red flag |
|---|---|
| Anon SELECT returns rows | Public data leak / missing RLS |
| Authenticated user reads other users' rows | IDOR / weak USING clause |
| INSERT/UPDATE/DELETE as anon | Anon write |
INSERT as user A with user_id of B | Missing WITH CHECK |
| PATCH role/is_admin/plan columns | Mass assignment / broken column grants |
Prefer Prefer: count=exact and small limit — enough for PoC, not dumps.
2B. RPC / functions
POST /rest/v1/rpc/<fn>
Probe discovered function names. SECURITY DEFINER without auth checks often = privilege escalation.
2C. Auth / JWT
- Sign up two accounts; compare access to the same resources.
- If policies use roles from JWT: try
updateUser({ data: { role: 'admin' } })(user_metadata is user-writable). - Confirm whether admin/premium flags live in
user_metadata(Critical) vsapp_metadata/ server table.
2D. Storage
GET /storage/v1/bucket
GET /storage/v1/object/list/<bucket>
- Public buckets with sensitive objects
- Upload to buckets as anon/authenticated when not intended
- Path traversal / predictable object names / IDOR on object paths
2E. Realtime
- Subscribe to sensitive tables/channels as anon or low-priv user.
- Presence of unrestricted channels for private data.
2F. Edge Functions
- Call
/functions/v1/<name>with anon key. - Missing JWT verification, SSRF, secrets in responses, IDOR in function logic.
2G. Secrets in client
service_rolein JS/mobile → Critical.- Database password / connection string in client → Critical.
- Long-lived user tokens in logs → High/Medium depending on impact.
Phase 3 — Catalog SQL audit (when DB access exists)
Read-only. Do not run DDL/DML "fixes" during bounty unless asked for remediation on owned projects.
| Access | Script |
|---|---|
| Supabase SQL Editor | references/audit-dashboard.sql (no \echo) |
psql | references/audit-psql.sql |
| Deep pass / PT-BR red flags | references/audit-complete.sql |
Run sections in order. Capture scorecard + every CRITICAL/WARN row.
Interpret with references/misconfig-catalog.md.
Phase 4 — Abuse validation
For each candidate finding, prove impact with a minimal PoC:
- Precondition (keys, account state)
- Request(s) (HTTP or SQL) — redact secrets in final writeup if required
- Observed result (status, sample fields, row counts — not full dumps)
- Impact (confidentiality / integrity / privilege)
- False-positive check (public-by-design marketing data? documented public bucket?)
Prioritize:
- service_role leak, RLS off on tenant tables, USING(true) on private data
- user_metadata authorization, anon write, SECURITY DEFINER without checks
- Storage IDOR, missing WITH CHECK, horizontal IDOR
- Info disclosure (schema, enums, error messages), missing FORCE RLS
Phase 5 — Report
Write supabase-security-report-YYYY-MM-DD.md using references/report-template.md.
Severity guide (adjust to program CVSS):
| Severity | Examples |
|---|---|
| Critical | service_role in client; RLS off + PII/tenant data; USING(true) private tables; user_metadata → admin |
| High | Anon write on sensitive tables; DEFINER RPC priv-esc; Storage full read of private files |
| Medium | IDOR on non-critical objects; missing WITH CHECK; app_metadata misuse if settable |
| Low | Verbose errors; enum/role name leak; RLS perf anti-patterns |
| Info | Hardening notes (FORCE RLS, indexes, Security Advisor) |
Each finding: title, severity, asset, evidence, PoC, impact, remediation, references (Supabase docs / OWASP).
Common vibe-coder failure modes (hunt these first)
- RLS never enabled on
publictables exposed via PostgREST CREATE POLICY ... USING (true)"so the app works"- Authorization via
auth.jwt() -> user_metadata/raw_user_meta_data - No WITH CHECK on INSERT/UPDATE (owner spoofing)
- Anon policies for write left from prototypes
- SECURITY DEFINER RPCs granted to
anon/authenticatedwithoutauth.uid()checks or pinnedsearch_path - Storage buckets public with user uploads
- service_role in Next.js
NEXT_PUBLIC_*or mobile apps - Views without
security_invokerbypassing RLS - Realtime on tables that REST already protects poorly
Full catalog: references/misconfig-catalog.md.
Remediation patterns (for owned projects or report "fix")
-- Enable + force RLS
ALTER TABLE public.t ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.t FORCE ROW LEVEL SECURITY;
-- Owner-only pattern
CREATE POLICY t_select_own ON public.t
FOR SELECT TO authenticated
USING (user_id = (SELECT auth.uid()));
CREATE POLICY t_insert_own ON public.t
FOR INSERT TO authenticated
WITH CHECK (user_id = (SELECT auth.uid()));
- Roles: table
user_roleswith its own RLS, orapp_metadataset only by service_role / Auth Hook — neveruser_metadatafor authz. - DEFINER functions:
SET search_path = public, pg_temp, explicit authz, minimal GRANT. - Re-run Supabase Dashboard → Database → Security Advisor after fixes.
Resource files
| Path | Use |
|---|---|
references/audit-dashboard.sql | Catalog audit in SQL Editor |
references/audit-psql.sql | Catalog audit via psql (\echo sections) |
references/audit-complete.sql | Extended PT-BR checklist + executive summary query |
references/misconfig-catalog.md | Red flags, severity, PoC hints |
references/report-template.md | Report skeleton |
Constraints
- Read-only preference; no destructive tests without explicit user request.
- Minimize data access; PoC-sized evidence only.
- Do not commit real keys, JWTs, or PII into git.
- Coordinate with
ethical-redteam/vuln-discoverywhen the engagement is broader than Supabase.