strix/skills/technologies/supabase.md
Security testing for Supabase applications. Focus on mis-scoped Row Level Security (RLS), unsafe RPCs, leaked service_role keys, lax Storage policies, and Edge Functions trusting headers without binding to issuer/audience/tenant.
Data Access
Storage
Authentication
Server-Side
Endpoints
https://<ref>.supabase.co/rest/v1/<table>https://<ref>.supabase.co/rest/v1/rpc/<fn>https://<ref>.supabase.co/storage/v1https://<ref>.supabase.co/graphql/v1wss://<ref>.supabase.co/realtime/v1https://<ref>.supabase.co/auth/v1https://<ref>.functions.supabase.co/Headers
apikey: <anon-or-service> — identifies projectAuthorization: Bearer <JWT> — binds user contextRoles
anon, authenticated — standard rolesservice_role — bypasses RLS, must never be client-exposedKey Principle
auth.uid() returns current user UUID from JWT. Policies must never trust client-supplied IDs over server context.
SECURITY DEFINER)service_role accessEnumerate Surfaces
/rest/v1/<table>
/rest/v1/rpc/<fn>
/storage/v1/object/public/<bucket>/
/storage/v1/object/list/<bucket>?prefix=
/graphql/v1
/auth/v1
Obtain Principals
service_role key leaked in client bundle or Edge Function responsesEnable RLS on every non-public table; absence or "permit-all" policies → bulk exposure.
Common Gaps
auth.uid() for SELECT but forget UPDATE/DELETE/INSERTorg_id/tenant_id) allow cross-tenant accessuser_id in payload) instead of JWTTests
# Compare row counts for two users
GET /rest/v1/<table>?select=*&Prefer=count=exact
# Cross-tenant probe
GET /rest/v1/<table>?org_id=eq.<other_org>
GET /rest/v1/<table>?or=(org_id.eq.other,org_id.is.null)
# Write-path
PATCH /rest/v1/<table>?id=eq.<foreign_id>
DELETE /rest/v1/<table>?id=eq.<foreign_id>
POST /rest/v1/<table> with foreign owner_id
Filters
eq, neq, lt, gt, ilike, or, is, inselect=*,profile(*)—exploits overfetch if resolvers skip per-row checksLIKE/ILIKE filters combined with missing RLS → mass disclosure via wildcard queriesHeaders
Prefer: return=representation — echo writesPrefer: count=exact — exposure via countsAccept-Profile/Content-Profile — select schemaIDOR Patterns
/rest/v1/<table>?select=*&id=eq.<other_id>
/rest/v1/<table>?select=*&slug=eq.<other_slug>
/rest/v1/<table>?select=*&email=eq.<other_email>
Mass Assignment
RPC endpoints map to SQL functions. SECURITY DEFINER bypasses RLS unless carefully coded; SECURITY INVOKER respects caller.
Anti-Patterns
SECURITY DEFINER + missing owner checks → vertical/horizontal bypassset search_path left to public; function resolves unsafe objectsuser_id/tenant_id rather than auth.uid()Tests
# Call as different users with foreign IDs
POST /rest/v1/rpc/<fn> {"user_id": "<foreign_id>"}
# Remove JWT entirely
Authorization: Bearer <anon_token>
Verify functions perform explicit ownership/tenant checks inside SQL.
Buckets
storage.objects with RLS-like policiesMisconfigurations
# Public bucket with sensitive data
GET /storage/v1/object/public/<bucket>/<path>
# List prefixes without auth
GET /storage/v1/object/list/<bucket>?prefix=
# Signed URL reuse across tenants/paths
Content-Type Abuse
text/html or image/svg+xmlX-Content-Type-Options: nosniff and Content-Disposition: attachmentPath Confusion
.. segments may be rejected at UI but accepted by APIEndpoint: wss://<ref>.supabase.co/realtime/v1
Risks
Tests
public:realtime changes on protected tables; confirm visibility aligns with RLSroom:<user_id>, org:<org_id>Endpoint: /graphql/v1 using pg_graphql with RLS
Risks
Tests
GoTrue issues JWTs with claims (sub=uid, role, aud=authenticated).
Verification Requirements
Pitfalls
apikey as identity (it's project-scoped, not user identity)service_role key in client bundle or Edge Function responsesTests
Deno-based functions often initialize Supabase client with service_role.
Risks
Tests
Ensure every query joins or filters by tenant_id/org_id derived from JWT context, not client input.
Tests
application/json ↔ application/x-www-form-urlencoded ↔ multipart/form-dataPrefer: count=exact and ETag/length diffs to infer unauthorized rowsIf-None-Match) to detect object existenceservice_role leaksor=, ilike, neq, is.null)