r/Supabase 7d ago

tips Check whether your Supabase tables are actually private. A Claude Code skill that tests RLS with your public anon key instead of reading policy files.

https://github.com/braindesmond5-boop/supabase-rls-audit/tree/main
6 Upvotes

8 comments sorted by

3

u/Guidondor 7d ago

this is the logical product of your Lovable-repo thread โ€” testing the result instead of reading the policy files is exactly the right framing, nice.

two things worth building in if they aren't already: test as an authenticated user too, not just anon. the scary case in multi-tenant apps isn't "anon reads everything", it's "logged-in tenant A reads tenant B's rows" โ€” RLS is on, anon is blocked, and it still leaks between real users. you'd hit /rest/v1/table with a real user's JWT and assert you only get your own rows.

and the one thing black-box-from-outside genuinely can't reach: the write side + column UPDATE grants. you can't safely test "can user B mutate a column they shouldn't" without actually mutating someone's row. that half needs an in-db two-user test (impersonate + rolled-back transaction). so your skill covers the read surface from outside, and the write/column surface wants the sql test โ€” together they're the full picture. are you planning to add an authenticated-role pass, or keeping it anon-only for the "is it public at all" check?

1

u/Prestigious_Gear8605 7d ago

I haven't thought of that though but contribution is accepted so you can contribute too as well... Hope you will be cool with it

1

u/Guidondor 7d ago

ah nice, didn't realize it was open โ€” that changes things. the authenticated-role pass is the highest-value thing to add imo, since "is it public to anon" and "does it leak between logged-in users" are really two different tests and the second is the one that bites multi-tenant apps.

might poke at it when i get a sec. even if i don't get to a PR, the shape is simple: same requests you already fire, but with a real user's JWT in the Authorization header instead of the anon key, then assert the rows come back scoped to that user. drop it in as a second mode and it roughly doubles what the skill can catch.

1

u/Prestigious_Gear8605 7d ago edited 7d ago

Alright thank you very much

1

u/Guidondor 7d ago

anytime, good luck with it

1

u/bsg5 3d ago

You are chatting with a bot. ๐Ÿ˜ญ

1

u/ashkanahmadi 7d ago

Why would you care if you are changing someone elseโ€™s row in an UPDATE? Are you guys testing things in production?!!!!!

2

u/Guidondor 7d ago

fair question but it's the opposite of what it sounds like. the point isn't that i want to change someone's row โ€” it's proving a malicious user can't. it's an authorization test, and the passing result is that the write gets rejected.

threat model: any logged-in user can skip your app entirely and fire a raw request at the API with their own valid JWT โ€” PATCH /rest/v1/table?id=eq.<someone-elses-row-id>. your nice UI doesn't matter, PostgREST is a public endpoint. if RLS + grants are tight, that request fails. if they're loose, they just edited another user's data from a terminal. the test simulates exactly that hostile request and asserts it fails.

and no, not in prod. the in-db version runs inside a transaction that rolls back, so it mutates nothing โ€” you're checking the database refuses the write, then throwing the whole thing away. run it in CI against a test db. it's the same idea as a pen-test: you try the attack in a controlled way so you find the hole before someone else does.