Does Supabase RLS cover TRUNCATE after GRANT ALL? No. Here's how to revoke it
Short answer: No. Row level security never applies to TRUNCATE. If anon or authenticated holds the TRUNCATE privilege, from a grant all or from the old default grants on tables made before October 30, 2026, your policies don't protect against it. Revoke it. To see what your own live site gives away first, run a free SniffPup scan (SniffPup is a free passive security scanner for vibe-coded apps). Only check sites and projects you own.
Home › Guides › GRANT ALL, TRUNCATE and RLS · Last updated · By Ember, SniffPup
The fast way: a free SniffPup scan runs 15 checks from the outside, including Supabase read access using the public config your site already ships. No signup. Ember can sniff what strangers can read, but she can't see your table grants, so run the query below too.
A 100 means no issues were found in our checks. It is never proof of security.
1. Why RLS doesn't cover TRUNCATE
The Postgres row security docs say it plainly: operations that apply to the whole table, such as TRUNCATE and REFERENCES, are not subject to row security. Policies filter rows for select, insert, update and delete. TRUNCATE empties the whole table without looking at rows, so the only thing that stops it is the TRUNCATE privilege itself.
2. Where the privilege comes from
Per the Postgres GRANT docs, ALL PRIVILEGES on a table includes TRUNCATE, REFERENCES and TRIGGER. AI builders often add this line to a migration to make a permission error go away:
grant all on table public.orders to anon, authenticated;
Since Supabase's October 30, 2026 change, new tables in public get no Data API grants, so a fresh table throws a 42501 error until you grant something. That is exactly when grant all gets pasted in, and TRUNCATE comes along with it. Older tables kept their grants: as supabase/supabase#48382 shows, a table made with plain create table under the old defaults has DELETE, INSERT, REFERENCES, SELECT, TRIGGER, TRUNCATE and UPDATE for both roles. The Supabase RLS guide says to grant only what each role needs.
3. How big is the risk?
Be calm about it. The REST API (PostgREST) has no TRUNCATE request, and a delete() from the browser still goes through your policies. The danger is any route that runs SQL as anon or authenticated: for example an rpc function that builds SQL from user input. There, one TRUNCATE wipes every row and RLS never gets a say. A privilege nobody needs is one you should take away.
4. Check which tables are affected
Run this in the SQL editor:
select table_name, grantee, string_agg(privilege_type, ', ') as privileges
from information_schema.role_table_grants
where table_schema = 'public'
and grantee in ('anon', 'authenticated')
and privilege_type in ('TRUNCATE', 'REFERENCES', 'TRIGGER')
group by table_name, grantee
order by table_name;Any row here is a table where those roles can truncate (or add references or triggers) no matter what RLS says.
5. Revoke it, and keep it revoked
-- existing tables revoke truncate, references, trigger on all tables in schema public from anon, authenticated; -- tables created later by postgres in public (projects on the old default grants) alter default privileges for role postgres in schema public revoke truncate, references, trigger on tables from anon, authenticated;
The second statement matters if your project still has the old automatic grants on. ALTER DEFAULT PRIVILEGES only changes tables created later by the role you name, so run it for each role that creates tables (usually postgres). Then replace any grant all in your migrations with the exact verbs you need, like grant select, insert, update, delete, and keep RLS on so those stay filtered by row. If a table is dropped and recreated, check it again: grants go with the table. Run the query from step 4: it should return nothing. For policy patterns, see Supabase RLS policy examples.
FAQ
Does Supabase RLS stop TRUNCATE?
No. The Postgres docs say operations that apply to the whole table, such as TRUNCATE and REFERENCES, are not subject to row security. RLS filters rows for SELECT, INSERT, UPDATE and DELETE. TRUNCATE is controlled only by the TRUNCATE privilege.
Does GRANT ALL include TRUNCATE?
Yes. On a table, ALL PRIVILEGES means SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES and TRIGGER (plus MAINTAIN on Postgres 17). So grant all on a table to anon gives the anon role TRUNCATE, whatever your policies say.
Can someone truncate my table with just the anon key?
Not through the REST API directly: PostgREST has no TRUNCATE verb, and a DELETE still goes through RLS. The risk is any path that runs SQL as anon or authenticated, such as an rpc function that builds SQL from input. The privilege shouldn't be there, so revoke it.
How do I fix it?
revoke truncate, references, trigger on all tables in schema public from anon, authenticated; then replace any grant all in your migrations with only the verbs you need, like grant select, insert, update, delete.
Does a clean SniffPup report mean my grants are right?
No. A score of 100 means no issues were found in our checks. It is never proof of security: an outside scan can't see table privileges, so run the query on this page too.
Sources: Postgres Row Security Policies, GRANT, TRUNCATE and ALTER DEFAULT PRIVILEGES docs, Supabase Row Level Security docs and changelog 45329, and supabase/supabase#48382. Checked November 1, 2026.
A score of 100 means no issues were found in our checks. It is not proof that your app is secure.