Public storage bucket
Files in this bucket are served to anyone with the link. ID scans and invoices don't belong there.
- Severity
- Medium
- Who can exploit it
- Anyone with, or able to list, the file links
- Found by
- Free RLS check (static)
- Lab proof
- Listing test included for your engineer
What it is
A public bucket serves its files to anyone who has the URL, with no sign-in. That is right for avatars and product photos. It is a leak when the bucket holds personal files: ID documents, CVs, invoices, signed contracts, medical letters.
It gets worse when a broad read policy on storage.objects also lets visitors list the bucket, because then nobody needs to guess the links.
Example of vulnerable code
Simplified from the patterns we see in AI-built apps. Synthetic: not taken from any real app.
insert into storage.buckets (id, name, public)
values ('documents', 'documents', true);
-- Often paired with a broad read policy that also allows listing
create policy "Anyone can view documents"
on storage.objects for select
using (bucket_id = 'documents');
Check it yourself
-- 1. Which buckets are public?
select id, name, public from storage.buckets where public;
-- 2. Who can read objects (and so list file names)?
select policyname, roles, cmd, qual
from pg_policies
where schemaname = 'storage' and tablename = 'objects';
The fix PatchLabs writes
PatchLabs makes the bucket private and lets each user read only the files in their own folder. The app then shows files through short-lived signed links.
update storage.buckets set public = false where id = 'documents';
drop policy "Anyone can view documents" on storage.objects;
create policy "Owners read own documents"
on storage.objects for select
to authenticated
using (
bucket_id = 'documents'
and (storage.foldername(name))[1] = (select auth.uid())::text
);
The code change that ships with it: replace getPublicUrl(path) with createSignedUrl(path, 60) wherever the app shows these files, and upload to <user id>/filename. The report lists each place.
Every fix arrives as a migration file (plus any code change it needs, noted in the report) for your engineer to approve. Download it, or commit it on GitHub in one click from the report page. Automatic pull requests need our GitHub App, which is coming.
The proof it runs
[BEFORE] anon list documents → 3 LEAK [AFTER] anon → 0 objects DENIED stranger → 0 objects DENIED owner → 1, own folder OK
The listing test reads storage.objects as each identity. It is included as SQL for your engineer; the automatic proof today covers your tables. Whether a public URL still works is decided by the bucket's public flag, which the fix sets to false.
When the pattern is intended
Public buckets are right for files meant for everyone: avatars, product images, logos, blog images. Keep them public, and keep personal files in a separate private bucket. If a public bucket only holds public assets, there is nothing to fix.
Supabase linter
- 0025 public_bucket_allows_listing
- Flags public buckets whose object policies let clients list their files.
Related findings
Anonymous visitor can read personal data
Anyone with your app's public key can read emails, phone numbers or addresses.
PL-008Row level security not enabled
No rules at all. With API access granted, anyone with the public key can read and change every row.
PL-011Key still in git history
You deleted the key. Every old commit still has it, and so does every clone and fork.
Guides
- How to secure a Lovable app’s Supabase database
- Supabase storage bucket policies: public, private and per-user folders
Free check
Does your app have PL-007?
Paste your public repo. We rebuild the database from your migrations, test it as a visitor, a stranger and the owner, and hand you the proven fix.
Last reviewed 10 Nov 2026. Finding IDs are stable: link to them freely. Spotted a mistake? Tell us.