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: supabaseAdmin in 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:

  • INSERT rows with user_id = victim_id → impersonation
  • UPDATE their row → change user_id to victim's → own victim's data
  • DELETE any 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 DEFINER function 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"