4 Security Holes Your AI-Generated Supabase App Has (Lovable, Cursor, Bolt, v0)
You prompted: "Build me a SaaS with auth, database, and billing."
The AI delivered. Green checks everywhere. You deployed.
But did it secure the data layer?
I've audited 50+ vibe-coded Supabase apps. The same 4 holes appear in ~80% of them — not because AI is "bad at security," but because security requires adversarial thinking, not just implementation.
Hole 1: Service Role Key in Client Bundle
The code AI writes:
// lib/supabase.ts export const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE_KEY // 💀 COMMITTED TO REPO ) Why it happens: AI sees SUPABASE_SERVICE_ROLE_KEY in .env.example, assumes it's for the client. It's not — it's root database access.
Impact: Anyone extracts the key from your JS bundle → full DB read/write/delete, manage auth users, access storage.
Fix:
- Server-only:
supabaseAdminin Edge Functions / API routes only - Never
NEXT_PUBLIC_prefix on service role - Rotate immediately if ever committed: Supabase Dashboard → Settings → API → "Reset service_role key"
Hole 2: RLS Enabled, But using (true) Policy
The migration AI generates:
ALTER TABLE documents ENABLE ROW LEVEL SECURITY; CREATE POLICY "enable_read" ON documents FOR SELECT USING (true); -- 💀 EVERYONE READS EVERYTHING Why it happens: AI gets "permission denied" error during dev, adds permissive policy to unblock. Forgets to tighten.
Impact: Green "RLS Enabled" shield in dashboard. anon role reads all rows. Multi-tenant = cross-tenant leak.
Fix:
-- Owner-only read CREATE POLICY "owner_read" ON documents FOR SELECT USING (auth.uid() = user_id); -- Multi-tenant read CREATE POLICY "org_read" ON documents FOR SELECT USING (org_id = current_org_id()); Hole 3: Missing Write Policies (Silent Data Poisoning)
What AI implements:
-- Only SELECT policy CREATE POLICY "owner_read" ON documents FOR SELECT USING (auth.uid() = user_id); What's missing: INSERT, UPDATE, DELETE policies.
Impact: Any authenticated user can:
-
INSERTrows withuser_id = victim_id→ impersonation -
UPDATEtheir row → changeuser_idto victim's → own victim's data -
DELETEany row → ransomware
All silently succeed (RLS filters on read, not write, without explicit policies).
Fix: Complete command coverage:
CREATE POLICY "owner_all" ON documents FOR ALL USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); Hole 4: Middleware Doesn't Forward Auth Cookies (Vercel Double Login)
The middleware.ts AI writes:
export async function middleware(request: NextRequest) { const supabase = createServerClient(/* ... */) const { data: { user } } = await supabase.auth.getUser() // user is null in production! 💀 } Why it happens: Works locally. On Vercel, cookies aren't forwarded to Supabase client → getUser() returns null → auth breaks → users logged out randomly.
Fix: Explicitly forward auth cookies:
export async function middleware(request: NextRequest) { const response = NextResponse.next() const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll() { return request.cookies.getAll() }, setAll(cookiesToSet) { cookiesToSet.forEach(({ name, value, options }) => response.cookies.set(name, value, options) ) } } } ) await supabase.auth.getUser() // Refreshes session, writes cookies to response return response } The Pattern: AI Optimizes for "Working" Not "Secure"
| Dev Goal | AI Optimizes For | Security Gap |
|---|---|---|
| "Make auth work" | getUser() returns user | Cookie forwarding on Vercel |
| "Fix permission denied" | Policy that allows the query | using (true) = open door |
| "Add admin features" | Service role key usage | Key in client bundle |
| "Enable RLS" | ALTER TABLE ... ENABLE RLS | Only SELECT policy |
Free: Audit Your App in 30 Seconds
git clone https://github.com/cekuu35/supabase-rls-leak-demo cd supabase-rls-leak-demo npm install npm run audit 47 automated checks including all 4 holes above + 43 more:
- Service role key in client code
- RLS coverage per table
- Write policy completeness (
WITH CHECK) - Cookie forwarding in middleware
- Grant/policy mismatches
-
SECURITY DEFINERfunction safety
CTA
Free: Run audit → see your holes
$29 Kit: 47-check checklist + remediation SQL per finding
$50/mo Guard: CI gate fails build on any RLS gap
Previous in series: "Supabase RLS Not Working? Fix It by Symptom" | "RLS Write-Leak: The Silent Data Exposure"