"Vibe coding" has become the dominant trend in shipping speed for 2025. Developers use AI tools like Cursor, Windsurf, or v0 to generate entire features based on "vibes" or natural language prompts [2]. It feels like magic. You describe a SaaS dashboard, and the code appears.
But there is a hidden cost to this speed. AI models are trained on the internet's average code, not its most secure code. They prioritize functionality over safety.
Recent reports indicate that 49% of dependencies added by AI coding agents contain known vulnerabilities [3]. When you let an LLM architect your application, you often get "Vulnerability-as-a-Service" [2]. Common issues include hardcoded secrets, bypassed Row Level Security (RLS), and frontend-only validation logic that is easily circumvented.
If you refuse to ship vulnerable code, you cannot blindly trust the vibe. You must audit it.
Table of Contents
- The Vibe Coding Threat Model
- Audit Your Data Access Layer
- Validate Inputs (The AI Forgets This)
- Business Logic and Payment Flows
- Supply Chain and Dependencies
- Architecture as a Safety Net
The Vibe Coding Threat Model
When you write code manually, you (hopefully) understand the control flow. When you "vibe code," you act as a reviewer for a junior developer who works at lightning speed but doesn't understand security context.
The specific risks in a Next.js/Supabase stack usually fall into three categories:
- Hallucinated Authorization: The AI writes code that checks if a user is logged in but fails to check if they own the specific resource they are trying to edit [5].
- Client-Side Trust: AI tools often generate logic that performs sensitive operations (like calculating pricing) on the client side, which attackers can manipulate [3].
- Dependency Bloat: Agents pull in unmaintained or malicious packages to solve trivial problems [1].
Audit Your Data Access Layer
The most common vulnerability in AI-generated Supabase apps is the misuse of database clients. AI models frequently confuse the service_role (admin) key with the anon (public) key, or they bypass RLS entirely by running queries in contexts where policies don't apply.
Check every file where a Supabase client is instantiated. If you see process.env.SUPABASE_SERVICE_ROLE_KEY being used in a client component or passed to the browser, you have a critical leak [1].
The correct pattern isolates privileged access to the server. Here is how we handle this separation in the backend:
// lib/supabase/server.ts
// We explicitly separate the admin client (service role) from the user client (cookies)
import { createClient as createSupabaseClient } from '@supabase/supabase-js'
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
// DANGEROUS: Bypasses RLS. Use ONLY in Server Actions for system tasks.
export function createAdminClient() {
return createSupabaseClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{
auth: {
autoRefreshToken: false,
persistSession: false,
},
}
)
}
// SAFE: Uses user cookies and respects RLS policies.
export async function createServerClientWithCookies() {
const cookieStore = await cookies()
// ... implementation using createServerClient
}
If your AI generates a direct SQL query inside a React component using useEffect, rewrite it immediately. Data access belongs on the server or behind strict RLS policies [3].
Validate Inputs (The AI Forgets This)
AI tools are excellent at generating Zod schemas for UI forms but often forget to apply those schemas to the actual API route or Server Action. They assume frontend validation prevents bad data. It does not.
Attackers can bypass your React form and send raw requests to your server actions. You must validate the schema inside the action itself.
Here is the pattern to enforce:
// actions/auth.ts
'use server'
import { z } from 'zod'
import { rateLimit } from '@/lib/rate-limit'
// 1. Define the schema explicitly
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
})
export async function login(data: unknown) {
// 2. Add Rate Limiting (AI rarely generates this)
const { success } = await rateLimit('login', 5, 60)
if (!success) {
return { error: 'Too many attempts.' }
}
// 3. Validate on the SERVER, not just the client
const parsed = loginSchema.safeParse(data)
if (!parsed.success) {
return { error: 'Invalid input data' }
}
// Proceed with safe logic...
}
Audit your codebase for any Server Action that accepts an argument without immediately running .safeParse() or a similar validation check [4].
Business Logic and Payment Flows
Integrations like Stripe are high-risk areas for vibe coding. An AI might generate code that creates a checkout session but passes the price_id directly from the client without verification. This allows a user to swap a $100 product ID for a $1 product ID in the browser console.
Always re-verify the intent on the server. Furthermore, ensure that the customer ID is linked to your authenticated user on the backend, not via a client-provided ID.
Review your billing actions for this pattern:
// actions/billing.ts
'use server'
import { getStripe } from '@/lib/stripe/client'
import { getUser } from '@/lib/supabase/server'
export async function createCheckoutSession(data: { priceId: string }) {
// 1. Authenticate the user securely
const user = await getUser()
if (!user) redirect('/login')
// 2. Resolve the Stripe Customer ID from YOUR database,
// do not accept a 'customerId' from the client request.
const admin = createAdminClient()
const { data: customer } = await admin
.from('customers')
.select('stripe_customer_id')
.eq('id', user.id) // Enforce ownership
.single()
// ... create session
}
If your AI-generated code accepts userId or customerId as a function argument from the frontend, it is vulnerable to Insecure Direct Object Reference (IDOR) [3].
Supply Chain and Dependencies
The 49% vulnerability statistic for AI-generated dependencies is alarming [3]. AI coding agents often hallucinate packages that sound real but don't exist (leading to dependency confusion attacks) or pull in abandoned libraries with unpatched CVEs.
Run a rigorous audit on your package.json:
- Check for typosquatting: Did the AI install
react-hook-form-v7instead ofreact-hook-form? - Verify maintenance: Is the package actively maintained? AI's training data cuts off, so it often recommends libraries that were popular in 2021 but are dead in 2025.
- Use SCA tools: Integrate tools like Snyk or
npm auditinto your CI/CD pipeline to block builds with critical vulnerabilities [1].
Architecture as a Safety Net
The most effective way to secure vibe-coded apps is not to review every line, but to use an architecture that makes common errors impossible.
If you rely heavily on client-side Supabase calls, you must write perfect RLS policies for every table. If the AI misses one policy, your data is exposed.
SecureStartKit solves this by defaulting to a backend-only data access model. We perform data operations via Server Actions using the service_role key, but only after explicit server-side authentication and validation. This removes the anon key vector entirely for sensitive operations.
When you audit your app, ask yourself: "If I delete all my RLS policies, is my database wide open?" If the answer is yes, you are relying too heavily on configuration over architecture.
AI is a powerful accelerator. But you are the pilot. Do not let the autopilot fly you into a mountain.
References
- Using AI-generated code safely (Vibe coding security)— beaglesecurity.com
- Vibe check: The vibe coder’s security checklist for AI generated code— aikido.dev
- Source from fingerprint.com— fingerprint.com
- Vibe Coding: My Ultimate Checklist for Building Software with AI Magic - DEV Community— dev.to
- Vibe Coding Security Fundamentals | Wiz— wiz.io
- CISO Vibe Coding Checklist: Securing AI-Built Apps— aikido.dev
- Secure Vibe Coding Guide | Become a Citizen Developer | CSA— cloudsecurityalliance.org
Related Posts
Why 170+ Vibe-Coded Apps Got Hacked, And How to Actually Secure Your Supabase App
The Lovable hack exposed 170+ apps through missing RLS. Here's what went wrong and the exact steps to secure your Supabase database.
Why Security-First Matters for Your SaaS
Most SaaS templates expose your database to the browser. Here's why that's dangerous and how SecureStartKit does it differently.
Supabase Authentication in Next.js App Router: The Complete 2026 Guide
A production-ready guide to implementing secure, server-side authentication with Supabase and Next.js App Router, moving beyond outdated client-side patterns.