Skip to content

RLS policy trusts metadata the user can set

Scan rule · RSC-SEC-110

  • Security
  • Severity: Critical
  • CWE-639
  • OWASP A01-2021

Finds policies that check `user_metadata` or `raw_user_meta_data`, directly or through a helper function. Every user sets these values themselves.

Why this matters

Supabase stores two kinds of metadata per account: the server sets `app_metadata`, the user sets `user_metadata` — with one line in the browser, `supabase.auth.updateUser({ data: { role: 'admin' } })`. A policy that checks `auth.jwt() -> 'user_metadata' ->> 'role'` therefore asks the user whether they are an admin.

The mistake almost always appears while building an admin area: the role is written to the metadata at sign-up because that needs no extra table, and the policy reads it from there. Everything works in testing because nobody promotes themselves.

Fix: keep roles in a dedicated table (Lovable uses `user_roles` with `has_role()`) that only admins can write, or server-side in `app_metadata`. Then review every policy that reads `user_metadata` or `raw_user_meta_data`.

Scan tier

  • Repo access (deep scan)

How to fix it

Keep roles in `app_metadata` (server-side only) or in a dedicated `user_roles` table that only admins can write.

Scan your own app for free