SECURITY DEFINER functions in exposed schemas are not executable by anon or authenticated
Why it matters
Every function in an exposed schema is a public API endpoint: Supabase serves it at /rest/v1/rpc/<name> to anyone holding the anon key. A SECURITY DEFINER function runs with its owner's privileges, which bypass RLS, so a definer function that updates a balance, grants a role, deletes a user, or returns rows is an unauthenticated admin endpoint unless EXECUTE is revoked. Postgres grants EXECUTE on new functions to PUBLIC by default, and Supabase's default privileges additionally grant it to anon, authenticated, and service_role, so merely writing the function exposes it. AI tools love definer functions ('so RLS does not get in the way') and never revoke execute. Callers pass their own arguments, so a function taking a user_id parameter lets any visitor act as any user. The advisor reports this as lints 0028 (anon) and 0029 (authenticated).
Severity rationale
Critical because an executable SECURITY DEFINER function is an RLS-bypassing endpoint callable with the public anon key, so a single missed revoke can hand out reads or writes across every user.
Remediation
Either move the function out of the exposed schema or take EXECUTE away from the API roles. Revoke from public too, because Postgres grants it to PUBLIC by default, and from anon and authenticated explicitly because Supabase grants those by name:
revoke execute on function public.grant_credits(uuid, int) from public, anon, authenticated;
grant execute on function public.grant_credits(uuid, int) to service_role;
Better, create helper and admin functions in a private schema that is not exposed. Always give definer functions a fixed search path (set search_path = '') and have them read the caller from auth.uid() rather than a parameter. If an RPC is meant to be callable, add a -- INTENTIONALLY_CALLABLE: <reason> comment directly above CREATE FUNCTION.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Supabase RLS & Keys audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe:4.14 · CWE-284 — Improper Access Control
- cwe:4.14 · CWE-285 — Improper Authorization
- cwe:4.14 · CWE-269 — Improper Privilege Management
- owasp:2021 · A01 — Broken Access Control
- external · 0028_anon_security_definer_function_executable — Supabase database advisor lint 0028 anon can execute security definer function
- external · 0029_authenticated_security_definer_function_executable — Supabase database advisor lint 0029 authenticated can execute security definer function
Taxons
History
- 2026-10-03·v1.0.0·Initial supabase-rls-keys v1 authoring; SECURITY DEFINER functions in exposed schemas must not keep default EXECUTE for PUBLIC, anon, or authenticated, with deterministic detect-rg heuristic.·by supabase-rls-keys-v1-build