I Scanned 226 Supabase Apps and Found 4x More RLS Leaks Than Expected
A generalizable scanner pipeline for finding RLS-misconfigured Supabase apps via SERP discovery. Responsible disclosure methodology included.
A thread on r/Supabase this week from a developer who scanned 226 public Supabase-backed apps and found a 4x higher rate of RLS (Row Level Security) misconfiguration than expected. Many apps ship with default-allow policies, exposing user data via the anon key.
The methodology is generalizable: find Supabase-backed apps via SERP, hit their public endpoints, probe for RLS leaks. The tool you need is a search API that can pull public app URLs at scale.
The Scan Pipeline
- Query Google for Supabase-backed apps (e.g.
site:bolt.host supabase) - Extract URLs, fetch each app's public HTML
- Grep for Supabase anon keys exposed in the client bundle
- Test anon-key reads against common table names (
users,profiles,messages) - Flag apps that return data without auth
The Discovery Query
Different deploy platforms expose Supabase-backed apps at predictable URL patterns. Bolt.host, Lovable, Replit, Vercel previews, Netlify all have Supabase-adjacent signals in their HTML.
async function findSupabaseApps(deploymentPlatform: string) {
const query = `site:${deploymentPlatform} "supabase.co" OR "createClient"`;
const response = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: {
'x-api-key': process.env.SCAVIO_API_KEY!,
'content-type': 'application/json'
},
body: JSON.stringify({ query, num: 50 })
});
const data = await response.json();
return data.organic_results.map((r: any) => r.link);
}
const bolt = await findSupabaseApps('bolt.host');
const lovable = await findSupabaseApps('lovable.app');
// repeat across platformsThe Probe Step
Once you have the app URL and the anon key from its bundle, probing is a normal Supabase REST call. Ethical researchers stop here, log the finding, notify the developer. Don't touch data.
async function probeRls(appUrl: string, anonKey: string, table: string) {
const supabaseUrl = extractSupabaseUrl(appUrl);
const response = await fetch(
`${supabaseUrl}/rest/v1/${table}?select=*&limit=1`,
{
headers: {
apikey: anonKey,
Authorization: `Bearer ${anonKey}`
}
}
);
// If response returns data with the anon key, RLS is misconfigured.
// Count, don't exfiltrate.
return response.status === 200 ? 'open' : 'secured';
}Why This Is Worth Doing
Every major Supabase incident in the last two years has been RLS misconfig. It's the #1 self-inflicted wound in the ecosystem. A weekly scan of vibe-coded apps (Bolt, Lovable, Replit) finds dozens of new exposures every week because vibe-coding tools don't prompt users to configure RLS.
Responsible disclosure is the move. Most of these are hobby projects built by people who don't know about RLS; a friendly email with the fix link converts more gratitude than it does hostility.
The Ethics Line
Document, don't exfiltrate. Even accidental bulk-reads of user data creates legal liability and violates Supabase's ToS. The point is to verify the vulnerability exists, not to see the data.
Full scanner pipeline lives in the Supabase RLS scanner workflow. Runs weekly, emails findings to you and optionally to the developer.