Hi, and thanks for ElatoAI. I ran a static RLS analyzer (pgrls) over supabase/migrations and wanted to flag the policies on public.users — applying the migrations to a clean Postgres confirms the anon (unauthenticated) role has broad read and write access:
| policy |
cmd |
role |
predicate |
Enable read access for all users |
SELECT |
public |
USING (true) |
Update user table |
UPDATE |
anon |
USING (true) WITH CHECK (true) |
Concretely, with just the project's anon API key (no login):
- read — every row of
public.users is selectable.
- write — every row of
public.users is updatable: an anonymous caller can rewrite any user's row, not only their own.
I suspect part of this is intentional for the device path — esp users can update devices is also anon-scoped, so the ESP32 likely talks to Supabase with the anon key. That's a reasonable pattern, but as written these policies are unscoped (USING (true)), so they apply to all rows for any anonymous caller rather than just the row that belongs to the calling device/user.
Worth scoping each to the owning row — e.g. USING (id = <the identifier the device/session carries>) — and moving anything that doesn't truly need to be public off the anon/public role. (Also, lower priority: no FORCE ROW LEVEL SECURITY on these tables, and the same USING (true) public-read shape on devices / languages / personalities.)
I held off on a PR because the right predicate depends on your device-auth model — happy to send one once you confirm which column ties a users row to its device/session and whether the public read is deliberate.
Hi, and thanks for ElatoAI. I ran a static RLS analyzer (pgrls) over
supabase/migrationsand wanted to flag the policies onpublic.users— applying the migrations to a clean Postgres confirms theanon(unauthenticated) role has broad read and write access:Enable read access for all usersUSING (true)Update user tableUSING (true) WITH CHECK (true)Concretely, with just the project's
anonAPI key (no login):public.usersis selectable.public.usersis updatable: an anonymous caller can rewrite any user's row, not only their own.I suspect part of this is intentional for the device path —
esp users can update devicesis alsoanon-scoped, so the ESP32 likely talks to Supabase with the anon key. That's a reasonable pattern, but as written these policies are unscoped (USING (true)), so they apply to all rows for any anonymous caller rather than just the row that belongs to the calling device/user.Worth scoping each to the owning row — e.g.
USING (id = <the identifier the device/session carries>)— and moving anything that doesn't truly need to be public off theanon/publicrole. (Also, lower priority: noFORCE ROW LEVEL SECURITYon these tables, and the sameUSING (true)public-read shape ondevices/languages/personalities.)I held off on a PR because the right predicate depends on your device-auth model — happy to send one once you confirm which column ties a
usersrow to its device/session and whether the public read is deliberate.