· via dev.to (home feed)
Supabase RLS guards rows but not columns, letting users grant themselves admin
A dev.to post from security tool SniffPup warns that Supabase RLS policies filter rows, not columns, so a writable role or is_admin field on a profiles table can be flipped to admin from the browser.

A post on dev.to by SniffPup, a security-checklist side project, warns of a Supabase pitfall that turns a routine row level security setup into a privilege escalation hole: RLS policies operate on rows and say nothing about which fields an update may touch. If a privilege marker such as role or is_admin lives on a profiles table that users can already edit, a signed-in user can set it from the browser using the public anon key.
RLS is row-scoped, not column-scoped
Supabase's standard defense for client-facing tables is row level security. A typical setup gives every user a single row in a profiles table, plus an update policy that checks only that the row belongs to the signed-in user. That check passes no matter what the payload contains. Changing your display name is allowed; on a misconfigured table, so is appending a privileged value for role to the very same request.
How the escalation plays out
Supabase exposes its Postgres database to browser clients through a generated REST API, with the anon key public by design and RLS acting as the gatekeeper. According to the post, when a role or admin column sits on a table users can update, nothing stops a client from including that column in an update to its own row. From the database's perspective the write is legitimate, because the policy verified row ownership and nothing else. Any application logic that later consults that column to decide permissions will then treat the attacker as an administrator: no credentials stolen, no policy bypassed.
What the post recommends
The audit it suggests takes two minutes: check whether the profiles table carries a role or admin column. If it does, pick one of two fixes — permit updates only on safe columns, or move role data to a table users cannot write. The post's accompanying guide on sniffpup.com includes the SQL. The piece is also openly promotional: SniffPup sends a daily two-minute check by email and offers to scan apps for similar gaps, and it is named after the author's black Pomeranian, Ember, who as the post's hook goes gave herself the top security job.
Why it matters
The failure mode is nasty precisely because everything looks correctly configured. Enabling RLS, confirming that users can see and edit only their own rows, and shipping is a common workflow, and it passes ordinary testing, because the exposure surfaces only when a client deliberately adds a privileged field to an update payload. The blast radius is application-level privilege escalation: a user minting admin rights in a single request, armed only with the public key every client already holds. The lesson generalises beyond Supabase — authorization has to be reasoned about per column as well as per row, and any field that encodes authority belongs outside tables that untrusted clients can reach.
- #supabase
- #postgresql
- #security
- #row-level-security
- #access-control