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"
Top comments (0)