Public Supabase Storage bucket
Anyone can list the contents of this Storage bucket without signing in, so every file it holds can be enumerated and downloaded.
Why it matters
Anyone can list the contents of this Storage bucket without signing in, so every file it holds can be enumerated and downloaded. Public buckets are a legitimate feature — the question is whether you meant this one to be public, and whether anything user-uploaded ended up in it.
How ShipReady detects it
Tests whether a Supabase project is readable by anyone. The project URL and anon key are read from the page's own JavaScript — both are published there by design. The anon key is then used exactly as a browser would use it. ──────────────────────────────────────────────────────────────────────────── WHAT THIS USED TO ASK, AND WHY IT MISSED THE APPS IT EXISTS FOR ──────────────────────────────────────────────────────────────────────────── The probe list was twelve hardcoded names — users, profiles, orders, posts. A bakery app whose tables are `bakery_orders` and `pickup_slots` was therefore invisible, however wide open it was, and the report said nothing. That is the whole finding class this check exists for: CVE-2025-48757 documents Lovable+Supabase apps shipping tables with RLS off or set to USING (true), readable by anyone holding the anon key the front end publishes. Resources are now DISCOVERED rather than guessed, from three sources, and each finding records which one found it: client-code `.from("bakery_orders")`, `.rpc("claim_slot")` and `/rest/v1/...` paths in the site's own bundles — the strongest signal, because the application itself names the resource it depends on. postgrest-schema the project's own OpenAPI document at /rest/v1/, which PostgREST publishes to any holder of the anon key. This is schema METADATA — path names — not data. common-name the original twelve, kept as a supplement so a project whose bundle is unreadable is no worse off than before. ──────────────────────────────────────────────────────────────────────────── NO ROW IS EVER RETRIEVED ──────────────────────────────────────────────────────────────────────────── The old probe was `GET ...?select=*&limit=1` and it stored the returned row in `evidence.observed_response`. It proved the point by holding a copy of the customer's data — an email address, an address, whatever the first row held — in our logs and our database. The probe is now `HEAD` with `Prefer: count=exact`. A HEAD response carries no body at all; PostgREST answers with `Content-Range: 0-0/1523`, which is the count and nothing else. So the finding says "1,523 rows are readable with a key published in your own JavaScript" and the scanner has not read one of them. Returning zero rows is still not a finding. A table an anon key can reach but whose rows are all filtered is what correctly configured RLS looks like, and it is indistinguishable from an empty table — so neither is reported. WRITE ACCESS IS NOT TESTED, deliberately. Establishing it means attempting an insert against a stranger's production database, and a table whose columns are all nullable would take the row. There is no non-destructive proof available from outside, so the finding states that read access was tested and write access was not, rather than implying both. The probe sequence is memoised on the discovered project, so a project referenced by ten pages is tested once rather than ten times.
Detection is deterministic. ShipReady reports this only when it observes the condition directly, and prefers to miss a real problem over inventing one. Rule version 1.3.0.
How to fix it
This is the prompt ShipReady puts in your report — written to be pasted straight into Cursor, Claude Code, or whichever assistant built the app.
One or more Supabase Storage buckets have public read enabled, so anyone can list and download their contents. Public buckets are a legitimate feature (e.g. public avatars) — verify nothing private is stored in them, and set any bucket holding user or sensitive files to private with RLS-based access policies.
Frequently asked questions
- What does "Public Supabase Storage bucket" mean?
- Anyone can list the contents of this Storage bucket without signing in, so every file it holds can be enumerated and downloaded.
- How serious is it?
- ShipReady rates this medium. Fix soon. Meaningfully weakens a defence or degrades how the site works.
- How do I fix it?
- Paste the fix prompt on this page into Cursor, Claude Code or your AI editor. It is the same prompt ShipReady puts in your report.
- Can I check my own site?
- Yes — ShipReady scans up to ten pages of any public site for free and reports this alongside every other check. The free report lists every issue it finds and shows full evidence and a fix prompt for the critical and high-severity ones; medium and low findings are counted and unlock on Pro.
Related checks
Run this check on your site
ShipReady checks this and 193 other things across up to ten pages of your site, with an AI-ready fix for each. Free, no signup.