service_role client trusts the user id from the request
Scan rule · RSC-SEC-140
- Security
- Severity: High
- CWE-639
- OWASP A01-2021
Finds server functions (edge functions, API routes, Express & co.) that access data or accounts with the service_role key and pick the record by an id from the body, URL or a header — without a prior getUser or getClaims check.
Why this matters
This is the most common backend pattern in a Lovable app: an edge function builds a client with `SUPABASE_SERVICE_ROLE_KEY`, reads `userId` from the request body and deletes or changes records with it. The key bypasses Row Level Security entirely — which rows are affected is decided solely by what the caller writes into the body.
From the outside this looks clean: the tables are protected by RLS, the browser gets nothing out. The gap sits one level deeper, in the function that deliberately bypasses the policies. Generic code scanners do not recognise this pattern as a breach of trust because they do not know the difference between the anon and the service_role key.
Fix: verify the token from the Authorization header with `supabase.auth.getUser(token)`, return 401 without a user and use `user.id` only. Where the function needs no elevated rights, build a per-user client with the public key and the forwarded Authorization header — then RLS does the checking.
Scan tier
- Source code from source maps
- Repo access (deep scan)
How to fix it
Derive the identity with supabase.auth.getUser(token) from the Authorization header and use only that id; use the service_role client only where there is no per-user scope.