The story
A week ago I built supabase-security, a small Node.js auditor that scans Supabase projects for over-permissive RLS policies. To test it, I scanned 100 random Supabase projects from GitHub.
22 out of 100 leaked user data anonymously. The pattern was consistent: dashboard says "RLS enabled ✅", policies say USING (true), anonymous curl returns the full table.
Then I ran the tool against my own production CRM (FitCRM, an e-commerce ops platform I've been running for 2 years). Found 14 critical leaks. Order tables open to anon. Storage buckets with predictable signed URLs. RPCs with SECURITY DEFINER bypassing RLS.
I wrote the postmortem yesterday.
After that, two thoughts:
- If this many random Supabase projects leak, what about every other BaaS?
- The "keyless" mode (parse repo + probe anon, no admin creds) is the magic — anyone can run it on any project, including ones they don't own.
What I shipped today
Five sister tools. All MIT, all on npm, all use the same --discover pattern:
1. supabase-security v0.4
npx supabase-security@latest --discover .
Parses from('table') / rpc('function') / storage.from('bucket') call sites, extracts SUPABASE_URL + SUPABASE_ANON_KEY from your repo, then probes the public REST API anonymously. Flags any table that returns rows.
2. pocketbase-security v0.2
npx pocketbase-security --discover .
Parses pb.collection('name') call sites, then hits /api/collections/{name}/records anon. Catches the legendary PocketBase footguns: empty rules (= fully public), @request.auth.id != "" (any signed-up user passes), || true leftover dev rules.
3. appwrite-security v0.2
npx appwrite-security --discover .
Parses databases.listDocuments(dbId, collId), probes /v1/databases/{db}/collections/{coll}/documents anon. Flags collections with the any role on Read permission, or users role with document security OFF.
4. firebase-security v0.2
npx firebase-security --discover .
Parses collection(db, 'x') / doc(db, 'x/y'), extracts projectId from firebase config or env, then GETs firestore.googleapis.com/v1/projects/{pid}/databases/(default)/documents/{collection} anon. Confirms the legendary if true / wildcard-match-all leak pattern.
5. nhost-security v0.2
npx nhost-security --discover .
Parses gql`queries for table names, auto-resolves the Hasura endpoint from subdomain+region (or env), POSTs anonymous queries against/v1/graphql. Flags tables where theanonymous` role has SELECT with permissive row filter.
The common pattern across every BaaS
Every BaaS has the same trap:
| BaaS | Footgun | Dashboard says |
|---|---|---|
| Supabase |
USING (true) on RLS policy |
"RLS enabled ✅" |
| PocketBase | Empty rule string | "Public" (in tiny text) |
| Appwrite |
any role on Read |
Permissions "configured" |
| Firebase | allow read: if true |
"Test mode" |
| Nhost |
anonymous role with {} filter |
"Permission configured" |
In every case, the dashboard makes the configuration look intentional. In every case, anonymous curl returns rows. The dashboards don't show you what an anonymous-role evaluation actually returns — only what your role (the developer, signed in) returns.
Why "keyless --discover" is the unlock
Traditional security audits need admin credentials. The dev says "audit my Supabase" and hands you a service-role key. That's a high-trust ask and creates friction.
The keyless mode flips it:
- Run
npx <stack>-security --discover .from any project directory - It reads your client code (which already knows what tables/collections/buckets exist)
- It probes the public API with the same anonymous credentials anyone on the internet has
- No admin auth, no key sharing, no install
You can run it against your own project before pushing, or against a friend's project they're worried about, or in a CI step.
Patterns I'd add next
Things I want to detect but don't yet:
- Cross-platform: Same project using Supabase + Stripe + Resend — credential leak in one breaks all three. Need a unified scanner.
- JWT signature secrets committed to repos (already detectable via gitleaks but I want the BaaS-specific impact analysis)
- Storage paths with predictable signed URLs — the FitCRM case
- Function call sites with SECURITY DEFINER bypassing RLS — PostgREST anti-pattern
- GraphQL introspection on Hasura instances exposing schema
Try it
Pick the stack you use most. The discover mode runs in seconds:
`bash
cd your-project
npx supabase-security --discover . # or pocketbase-security, etc
`
If you have a project worth a deeper audit, the paid $99 single-tenant audit report includes the manual review I did on my own CRM that found the SECURITY DEFINER RPCs and the predictable bucket paths. But the free CLI catches the common 70% of leaks.
Links
- supabase-security: github · npm
- pocketbase-security: github · npm
- appwrite-security: github · npm
- firebase-security: github · npm
- nhost-security: github · npm
All MIT. If you find issues, open a PR. If you find a leak in your own project — fix it before someone else does.
If you run --discover on your project and want a second pair of eyes on the findings, I do one-off paid audits ($99 single project, $249 multi-tenant). But honestly, the CLI catches most of it.
Top comments (0)