Supabase security audit
Skill hlsitechio/claude-skills-security/saas-security-pack/supabase-security-audit
Defensive security audit skills for Claude — tech-stack-keyed and audit-domain-keyed packs for SaaS apps.
npx -y skills add hlsitechio/claude-skills-security --skill supabase-security-auditAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 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
Audit Supabase project security including Row-Level Security (RLS) policies, SECURITY DEFINER functions, anon/authenticated role grants, service_role key exposure, edge function authentication, and JWT verification on edge endpoints. Use this skill whenever the user mentions Supabase, RLS, row-level security, SECURITY DEFINER, anon role exposure, service_role leak, supabase-js client, edge functions auth, or asks "is my Supabase project safe". Trigger on phrases like "audit my Supabase", "review my RLS", "is RLS enabled", "SECURITY DEFINER risk", "anon role grants", "edge function security", "service role exposure", "Postgres function audit". Use this even if only one sub-topic is mentioned.
SKILL.md
8.0 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
Supabase Security Audit
Audit the security posture of a Supabase project across its three exposed surfaces: PostgREST (auto-API over Postgres), Storage, and Edge Functions. Defensive find-and-fix focus.
When this skill applies
- Reviewing whether RLS is enabled and policies are correct
- Auditing SECURITY DEFINER functions exposed to anon or authenticated roles
- Checking grants on
anonandauthenticatedroles - Looking for
service_rolekey exposure in client code - Reviewing edge function code for missing JWT verification
- Investigating suspicious activity in a Supabase project (post-incident)
Use other skills for: general app-code review (saas-code-security-review), multi-tenant patterns (saas-tenant-isolation).
Workflow
Follow ../_shared/audit-workflow.md. Skill-specific notes below.
Phase 1: Scope confirmation
Confirm with the user:
- Project ref (
xxxxx.supabase.co) - Do you have the service_role key available for read-only audits? (Some checks require it.)
- Are you the owner/admin of the project?
- Is this a production project or a staging/dev project?
Never request or store the service_role key in chat. Ask the user to run queries themselves using the SQL editor and paste results.
Phase 2: Inventory
Run the queries in scripts/inventory.sql against the project. They produce:
- Every table with RLS status
- Every policy per table
- Every function with
security definerstatus and grants - Every grant on
anonandauthenticated - Every storage bucket and its public/private status
- Edge function list (via CLI:
supabase functions list)
Phase 3: Detection — the checks
RLS coverage — see references/rls-patterns.md
- SUPA-RLS-1 Every user-data table has RLS enabled (
relrowsecurity = true). - SUPA-RLS-2 Every user-data table has at least one policy (RLS enabled with no policies = nothing is allowed; usually a config error).
- SUPA-RLS-3 Policies use
auth.uid()/auth.jwt()for identity, notcurrent_user(which is the Postgres role, not the app user). - SUPA-RLS-4 Policies cover all four operations (SELECT, INSERT, UPDATE, DELETE) — a permissive SELECT with no UPDATE policy means no UPDATE is allowed (often intended), but the audit should confirm intent.
- SUPA-RLS-5 Restrictive policies (
AS RESTRICTIVE) used where multiple permissive policies could otherwise OR together unsafely. - SUPA-RLS-6
USINGclauses don't have subqueries that bypass RLS on referenced tables (recursive RLS pitfall). - SUPA-RLS-7 Junction/audit/log tables also have RLS — not just the obvious user-facing tables.
Bypass paths — see references/security-definer.md
- SUPA-SD-1 Every
SECURITY DEFINERfunction inventoried with its grants. - SUPA-SD-2 SECURITY DEFINER functions called by anon or authenticated have explicit authorization checks at the top.
- SUPA-SD-3
search_pathset explicitly in every SECURITY DEFINER function (SET search_path = ''or a hardened path), to prevent function-resolution hijacking. - SUPA-SD-4 No SECURITY DEFINER function returns data from tables the caller couldn't otherwise reach via RLS, unless that's the explicit intent and authorization is enforced inside.
- SUPA-SD-5 SECURITY DEFINER functions revoked from
publicby default; grants only to specific roles.
Role grants — see references/anon-role-exposure.md
- SUPA-AR-1
anonhas SELECT only on tables intended for public access. Any table exposing PII to anon → finding. - SUPA-AR-2
anonhas no INSERT/UPDATE/DELETE on user-data tables (use functions or authenticated grants). - SUPA-AR-3
authenticatedgrants are minimal — RLS does the policy work, but grants must allow it. - SUPA-AR-4 No grants directly to
public(covers everyone including future roles). - SUPA-AR-5 Sequence usage grants align with table grants (else INSERTs fail oddly or succeed by accident).
Service role exposure
- SUPA-SR-1 No client code (browser bundle, mobile app, public repo) imports the service_role key. Grep the codebase for the key prefix.
- SUPA-SR-2 Service role used only in server-side code, edge functions with explicit auth, or admin tooling.
- SUPA-SR-3 Service role key rotated in last 12 months and after any suspected exposure.
- SUPA-SR-4 No service_role key in environment variables of client-facing services (Vercel/Netlify "preview" environments that may leak).
Edge functions — see references/edge-functions-auth.md
- SUPA-EF-1 Every edge function that handles authenticated user data calls
supabase.auth.getUser(jwt)to verify and identify the caller. - SUPA-EF-2 Functions don't trust client-supplied user IDs; always derive from JWT.
- SUPA-EF-3
Verify JWTflag enabled in function settings unless explicitly intended to be public (webhook receivers). - SUPA-EF-4 Webhook receivers verify signatures (Stripe, Resend, custom) — see
saas-api-security/references/webhook-security.md. - SUPA-EF-5 Service role key not used inside edge function for queries that should respect RLS — use the user's JWT with the supabase client.
- SUPA-EF-6 Outbound HTTP requests from edge functions follow SSRF protections (see
saas-code-security-review/references/ssrf-patterns.md).
Storage
- SUPA-ST-1 Buckets marked public only when intent is public; default to private.
- SUPA-ST-2 Storage policies (RLS-style) defined for private buckets.
- SUPA-ST-3 Signed URL TTLs reasonable for use case (short for one-time access, longer for embedded content).
- SUPA-ST-4 No path-traversal-style key construction from user input without sanitization.
Auth configuration
- SUPA-AUTH-1 Email confirmation required (don't allow unconfirmed accounts to act).
- SUPA-AUTH-2 Rate limits configured on auth endpoints.
- SUPA-AUTH-3 Redirect URLs allowlist explicit (no wildcards that could allow OAuth redirect to attacker).
- SUPA-AUTH-4 Custom SMTP configured for production (Supabase's shared SMTP has reputation limits).
- SUPA-AUTH-5 MFA enabled on Supabase dashboard for project owners.
Phase 4: Triage
Critical class examples:
- RLS disabled on any table containing user data
- service_role key in a public repo or browser bundle
- SECURITY DEFINER function exposed to anon that returns all rows from a sensitive table
- Edge function performing privileged operations without JWT verification
Phase 5: Report
Use ../_shared/findings-schema.md. Prefix IDs with SUPA-. Include the table/function name and grant context in evidence.
Scripts
scripts/audit_rls.sql— Lists every table with RLS status and policy countscripts/find_definer_funcs.sql— Lists SECURITY DEFINER functions and their grantsscripts/anon_grants_report.sql— Lists everything granted toanonandauthenticated
Run these in the Supabase SQL editor.
References
references/rls-patterns.md— Common RLS policies (good and bad), recursion pitfallsreferences/security-definer.md— When to use SECURITY DEFINER, how to harden itreferences/anon-role-exposure.md— Whatanonshould and shouldn't havereferences/edge-functions-auth.md— JWT verification patterns for Deno edge functions
Assets
assets/rls-template.sql— Default-deny policy template for new tables