From 1920495d9b8d90bbbbe75a9912d3cfaa4981c650 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 11 Jul 2026 14:00:38 +0000 Subject: [PATCH] Changes Co-authored-by: wolfoftyreso-debug <250630591+wolfoftyreso-debug@users.noreply.github.com> --- ...6_677fb7f4-3e2b-4614-ba6b-5fe5b28ff48b.sql | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 supabase/migrations/20260711140036_677fb7f4-3e2b-4614-ba6b-5fe5b28ff48b.sql diff --git a/supabase/migrations/20260711140036_677fb7f4-3e2b-4614-ba6b-5fe5b28ff48b.sql b/supabase/migrations/20260711140036_677fb7f4-3e2b-4614-ba6b-5fe5b28ff48b.sql new file mode 100644 index 0000000..8c3038a --- /dev/null +++ b/supabase/migrations/20260711140036_677fb7f4-3e2b-4614-ba6b-5fe5b28ff48b.sql @@ -0,0 +1,29 @@ +-- Restrict direct execution of SECURITY DEFINER function has_role. +-- Switching to SECURITY INVOKER: when called as auth.uid() the user_roles +-- RLS policy already lets a user see (only) their own role rows, so +-- has_role(auth.uid(), 'admin') still returns correctly. +-- We also revoke EXECUTE from anon/public so the function cannot be +-- invoked directly from the Data API. + +CREATE OR REPLACE FUNCTION public.has_role(_user_id uuid, _role app_role) +RETURNS boolean +LANGUAGE sql +STABLE +SECURITY INVOKER +SET search_path = public +AS $$ + SELECT EXISTS ( + SELECT 1 + FROM public.user_roles + WHERE user_id = _user_id + AND role = _role + ) +$$; + +REVOKE EXECUTE ON FUNCTION public.has_role(uuid, app_role) FROM PUBLIC; +REVOKE EXECUTE ON FUNCTION public.has_role(uuid, app_role) FROM anon; +-- authenticated retains EXECUTE so RLS policies referencing has_role +-- continue to work; the function itself is now SECURITY INVOKER so it +-- cannot be used to bypass RLS. +GRANT EXECUTE ON FUNCTION public.has_role(uuid, app_role) TO authenticated; +GRANT EXECUTE ON FUNCTION public.has_role(uuid, app_role) TO service_role; \ No newline at end of file