Users can grant themselves privileges (self-promotion)
Scan rule · RSC-SEC-111
- Security
- Severity: Critical
- CWE-269
- OWASP A01-2021
Finds owner-bound UPDATE policies on tables with a privilege column (`role`, `is_admin`, `plan`, `credits`, `balance`, `verified` …) without column grants, without a `with check` comparison and without a protecting trigger.
Why this matters
A policy like `for update using (auth.uid() = id)` only answers WHOSE row may be changed — not WHICH columns. If the same row holds the role, the plan or the balance, the user simply changes it too: `update profiles set role = 'admin' where id = auth.uid()`, sent from the browser.
Row Level Security has no notion of columns. Protection has to come from elsewhere: column privileges (`grant update (display_name) …`), a `with check` comparison against the previous value, or a trigger. A bare `revoke update (role)` does nothing while the table-level privilege remains.
We report this from your migrations when none of these three protections can be found for the column. The cleanest fix is not to keep roles in the profile table at all, but in a separate table only admins can write.
Scan tier
- Repo access (deep scan)
How to fix it
`revoke update on <table> from authenticated` and grant only the harmless columns via `grant update (…)` — or a BEFORE UPDATE trigger that rejects changes to the privilege column.