Lovable to Production: The 18-Point Security Checklist
CVE-2025-48757 exposed 10% of Lovable apps. This 18-point checklist covers RLS, auth, webhooks, and deployment — what Lovable doesn't generate.
What "Lovable to Production" Actually Means
Lovable to production is the process of transforming a working Lovable prototype into production-grade code that handles real users, real payments, and real security threats. This means adding the security, reliability, and observability patterns that Lovable's AI doesn't generate by default: Row-Level Security policies, production-grade authentication flows, webhook signature verification, structured error handling, and end-to-end test coverage. The output is software that fails safely under adversarial conditions — not just software that runs in demo mode.
This post is the 18-point checklist derived from CVE-2025-48757, VAS security audits, and the patterns Soatech implements during Production Lift engagements. Bookmark it. Reference it before every launch.
Why This Checklist Exists
Three data points establish why "Lovable to production" is now a defined market category:
CVE-2025-48757: The Wake-Up Call
In May 2025, security researchers disclosed CVE-2025-48757 — a vulnerability pattern affecting 10.3% of Lovable-built applications. The root cause: missing Supabase Row-Level Security (RLS) policies on user-facing tables.
Per HackNope's December 2025 audit, apps affected by CVE-2025-48757 allowed any authenticated user to read and modify data belonging to other users. A simple SELECT * FROM proposals query returned every proposal in the database — regardless of the requesting user's tenant.
Lovable's AI generates working Supabase queries. It does not generate the RLS policies that restrict those queries to the correct tenant.
Veracode's AI Code Security Numbers
Per Veracode's Spring 2026 GenAI Code Security report, 45% of AI-generated code contains security vulnerabilities even when syntax correctness exceeds 95%. The implication: the code runs but fails under adversarial conditions. Lovable code is syntactically correct, functionally incomplete.
The 80% Launch Vulnerability Rate
Per VAS (Vibe App Scanner), 80% of AI-built applications contain at least one exploitable vulnerability at launch. The five most common: missing RLS (44%), exposed service_role keys (23%), no rate limiting on auth endpoints (19%), missing HTTPS enforcement (11%), and leaked API keys in client bundles (8%).
The checklist below covers all five — plus 13 additional production-readiness gaps.
The 18-Point Lovable-to-Production Checklist
Organized by security domain. Items marked [CRITICAL] must be fixed before any user-facing launch. Items marked [HIGH] should be fixed within the first week of production. Items marked [MEDIUM] are technical debt that compounds if ignored.
Category 1: Data Security & Multi-Tenancy (4 items)
1. Enable Row-Level Security on Every User-Facing Table [CRITICAL]
What Lovable generates:
-- Lovable creates tables without RLS
CREATE TABLE proposals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id),
title TEXT,
content TEXT
);
What's missing:
-- Enable RLS
ALTER TABLE proposals ENABLE ROW LEVEL SECURITY;
-- Create isolation policy
CREATE POLICY tenant_isolation ON proposals
FOR ALL
USING (user_id = auth.uid());
Without RLS, any authenticated user can query any row in the table. This is CVE-2025-48757.
Test: Run SELECT * FROM proposals as a newly registered user. If it returns rows you didn't create, RLS is missing.
2. Never Expose service_role Key to the Client [CRITICAL]
The pattern Lovable sometimes generates:
// WRONG: service_role key in client code
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY! // DANGER
);
The service_role key bypasses all RLS policies. If it's in your client bundle, attackers can extract it from browser DevTools and access every row in your database.
The production fix:
- Client code uses
NEXT_PUBLIC_SUPABASE_ANON_KEYonly service_rolekey exists only in server-side code (API routes, Server Actions)- Environment variables are scoped correctly:
NEXT_PUBLIC_*for client, non-prefixed for server
Test: Search your codebase for service_role. If it appears in any src/ file that isn't a Server Action or API route, it's exposed.
3. Implement Tenant Context Injection for Server Operations [HIGH]
Why it matters: Even with RLS, server-side operations (background jobs, webhooks, admin scripts) need to set the tenant context before executing queries.
The production pattern:
// Set tenant context before any database operation
await supabase.rpc('set_current_tenant', { tenant_id: userId });
// Now queries are scoped to this tenant
const { data } = await supabase.from('proposals').select('*');
Test: Run a server-side cron job that queries user data. Does it correctly scope to a specific user, or does it return all users' data?
4. Audit All Direct Database Access Paths [MEDIUM]
What to check:
- Every Supabase client instantiation
- Every raw SQL query (
.rpc()calls) - Every import/export script
- Every migration file
Per VAS security research, 34% of data exposure incidents come from migration scripts and import tools that bypass application-layer tenant filtering.
Category 2: Authentication & Session Security (4 items)
5. Harden Password Reset Against Enumeration [CRITICAL]
What Lovable generates: A password reset flow that returns different responses for "email exists" vs "email doesn't exist."
Why it's a vulnerability: Attackers can enumerate valid email addresses by observing response differences. "No account found" vs "Reset link sent" reveals which emails are registered.
The production fix:
// Return identical response regardless of email existence
export async function resetPassword(email: string) {
const startTime = Date.now();
// Attempt reset (may or may not send email)
await supabase.auth.resetPasswordForEmail(email);
// Ensure consistent timing (prevents timing attacks)
const elapsed = Date.now() - startTime;
if (elapsed < 500) {
await new Promise(r => setTimeout(r, 500 - elapsed));
}
// Always return success message
return { message: 'If an account exists, a reset link has been sent.' };
}
Test: Submit a password reset for a random fake email. Does the response differ from a real email? Does the response time differ?
6. Implement Rate Limiting on Auth Endpoints [CRITICAL]
What Lovable generates: Auth endpoints with no request throttling.
Why it's a vulnerability: Without rate limiting, attackers can brute-force passwords at thousands of attempts per second. Per OWASP, auth endpoints should allow no more than 5-10 attempts per IP per minute.
The production fix (Vercel Edge Middleware):
// middleware.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '1 m'), // 10 requests per minute
});
export async function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/api/auth')) {
const ip = request.ip ?? '127.0.0.1';
const { success } = await ratelimit.limit(ip);
if (!success) {
return new Response('Too many requests', { status: 429 });
}
}
return NextResponse.next();
}
Test: Write a script that sends 100 login attempts in 10 seconds. Does the endpoint start returning 429s?
7. Configure Session Cookies Correctly [HIGH]
The production cookie configuration:
// next-auth or custom auth config
cookies: {
sessionToken: {
name: '__Secure-next-auth.session-token',
options: {
httpOnly: true, // Not accessible via JavaScript
sameSite: 'lax', // CSRF protection
path: '/',
secure: true, // HTTPS only
domain: '.yourdomain.com', // Locked to apex domain
},
},
},
What to verify:
httpOnly: true— prevents XSS from stealing sessionssecure: true— prevents man-in-the-middle interceptionsameSite: 'lax'or'strict'— prevents CSRFdomainlocked to apex — prevents subdomain leakage
Test: Open DevTools → Application → Cookies. Are all auth cookies marked HttpOnly and Secure?
8. Implement Magic-Link Security [HIGH]
If your app uses magic-link authentication (email sign-in links):
Requirements:
- Tokens must be single-use (consumed on first click)
- Tokens must expire (15 minutes max)
- Tokens must be cryptographically random (32+ bytes)
- Tokens must be hashed in the database (store
sha256(token), not plaintext)
Test: Click a magic link, then click it again. Does it still work? (It shouldn't.)
Category 3: Integration Security (3 items)
9. Verify Webhook Signatures [CRITICAL]
What Lovable generates:
// WRONG: No signature verification
export async function POST(req: Request) {
const event = await req.json();
if (event.type === 'checkout.session.completed') {
await processOrder(event.data.object);
}
return Response.json({ received: true });
}
Why it's a vulnerability: Any attacker who discovers your webhook URL can fire fake events. Without signature verification, your app will process them as legitimate.
The production fix:
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
export async function POST(req: Request) {
const sig = req.headers.get('stripe-signature');
if (!sig) {
return Response.json({ error: 'Missing signature' }, { status: 400 });
}
const body = await req.text();
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
} catch {
return Response.json({ error: 'Invalid signature' }, { status: 400 });
}
// Process verified event...
}
Test: Send a raw POST request to your webhook endpoint with fake Stripe event JSON. Does it process it? (It shouldn't.)
10. Implement Webhook Idempotency [HIGH]
Why it matters: Stripe (and most webhook providers) retry failed deliveries. Without idempotency, you'll process the same event multiple times — double-charging customers, sending duplicate emails, or creating duplicate records.
The production pattern:
// Check if already processed
const existing = await db.query(
'SELECT id FROM processed_events WHERE event_id = $1',
[event.id]
);
if (existing.rows.length > 0) {
return Response.json({ received: true, deduped: true });
}
// Process event
await processEvent(event);
// Mark as processed
await db.query(
'INSERT INTO processed_events (event_id, processed_at) VALUES ($1, NOW())',
[event.id]
);
Test: Send the same webhook event twice with the same event ID. Does it create duplicate records?
11. Secure All Third-Party API Keys [HIGH]
Audit every integration:
- Stripe keys (secret key server-only, publishable key client-safe)
- OpenAI/Claude API keys (server-only, never in client bundles)
- Email service keys (Resend, SendGrid — server-only)
- Analytics keys (usually safe for client, verify each provider)
Test: Run grep -r "sk_live\|sk_test\|OPENAI_API_KEY" src/ — are any non-public keys in client-accessible files?
Category 4: Frontend Security (3 items)
12. Set Security Headers [HIGH]
Required headers (via next.config.ts):
const securityHeaders = [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';",
},
];
export default {
async headers() {
return [{ source: '/:path*', headers: securityHeaders }];
},
};
Test: Run securityheaders.com against your deployed URL. Target grade: A or A+.
13. Implement CSRF Protection [HIGH]
For Server Actions (Next.js 14+):
Server Actions have built-in CSRF protection via the __next_action header. Verify it's not disabled.
For API routes:
import { csrf } from '@/lib/csrf';
export async function POST(req: Request) {
const token = req.headers.get('x-csrf-token');
if (!csrf.verify(token)) {
return Response.json({ error: 'Invalid CSRF token' }, { status: 403 });
}
// Process request...
}
Test: Submit a form from a different origin (use curl with no Origin header). Does it succeed? (It shouldn't.)
14. Sanitize User-Generated Content [MEDIUM]
If your app displays user-generated content (comments, bios, descriptions):
import DOMPurify from 'isomorphic-dompurify';
// Sanitize before rendering
const sanitizedContent = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href'],
});
Test: Submit <script>alert('xss')</script> as a comment. Does the alert fire when viewing?
Category 5: Deployment & Infrastructure (2 items)
15. Enforce HTTPS Everywhere [CRITICAL]
Vercel enforces HTTPS by default. If you're deploying elsewhere:
// middleware.ts
export function middleware(request: NextRequest) {
if (request.headers.get('x-forwarded-proto') !== 'https') {
return NextResponse.redirect(
`https://${request.headers.get('host')}${request.nextUrl.pathname}`,
301
);
}
return NextResponse.next();
}
Test: Navigate to http://yourdomain.com — does it redirect to HTTPS?
16. Configure Environment Variables Correctly [HIGH]
The production pattern:
NEXT_PUBLIC_*— visible to client, safe for non-secrets- Non-prefixed variables — server-only, safe for secrets
- Never commit
.envfiles to git - Use Vercel/platform environment variable management
Test: In your deployed app, open DevTools → Sources → search for your Stripe secret key. Does it appear? (It shouldn't.)
Category 6: Testing & Observability (2 items)
17. Add End-to-End Tests for Critical Flows [HIGH]
Minimum coverage for launch:
- User registration flow
- User login flow
- Password reset flow
- Core feature happy path (e.g., create/read/update/delete a proposal)
- Payment flow (if applicable)
The production pattern (Playwright):
// tests/auth.spec.ts
test('user can register and login', async ({ page }) => {
await page.goto('/register');
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="password"]', 'SecureP@ssw0rd!');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toContainText('Welcome');
});
Per the wintura.ai reference implementation, a production app should have 15-25 spec files covering all critical user journeys.
18. Wire Up Error Tracking [HIGH]
The production stack:
- Sentry for error tracking (capture unhandled exceptions + breadcrumbs)
- Vercel Analytics or PostHog for performance monitoring
- Structured logging for debugging (not
console.log)
// sentry.client.config.ts
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 0.1, // 10% of transactions
environment: process.env.NODE_ENV,
});
Test: Throw an unhandled error in production. Does it appear in Sentry within 60 seconds?
Self-Assessment: Is Your Lovable App Production-Ready?
Score yourself:
| Category | Items | Max Points |
|---|---|---|
| Data Security | 4 items | 20 points (5 each) |
| Authentication | 4 items | 20 points |
| Integration Security | 3 items | 15 points |
| Frontend Security | 3 items | 15 points |
| Deployment | 2 items | 10 points |
| Testing & Observability | 2 items | 10 points |
| Total | 18 items | 90 points |
Scoring:
- 80-90 points: Production-ready. Ship it.
- 60-79 points: High-risk gaps. Fix before accepting payments.
- 40-59 points: Multiple critical vulnerabilities. Do not launch.
- Below 40: Start over with a Production Lift.
The Production Lift: 18 Points in 1 Week
The Soatech Production Lift implements all 18 checklist items in one week for €3,500 fixed:
Included:
- Production-grade auth (NextAuth v5 or Clerk)
- Multi-tenant Row-Level Security (Postgres RLS)
- Webhook signature verification + idempotency
- Security headers + CSRF + rate limiting
- Playwright e2e test suite (≤15 spec files)
- Sentry + Vercel Analytics
- Vercel production deployment
- 30-day post-ship bug fix window
Scope cap: ≤30K LOC, ≤10 routes, standard React/Next.js stack.
The same playbook that shipped wintura.ai — applied to your Lovable prototype.
The Conversion Ladder
Not sure where to start?
Production Audit (€1,500, 3 days): Written diagnosis only — severity-ranked PDF report covering all 18 items. No code changes. If you commit to the Production Lift within 30 days, the €1,500 converts (net Lift cost: €2,000).
Production Lift (€3,500, 1 week): All 18 checklist items implemented. Walk-away ownership.
MVP Sprint (from €8,500, 4-8 weeks): If your prototype exceeds Lift scope caps or needs significant feature work beyond hardening.
Frequently Asked Questions
Why does Lovable generate insecure code?
Lovable optimizes for speed-to-demo, not security-by-default. Its AI is trained on public repositories where security patterns are inconsistently applied. Per Veracode's Spring 2026 report, AI coding assistants achieve 95%+ syntax correctness but only 55% security pass rates. The gap is structural.
How long does it take to fix all 18 items myself?
Per Geminate Solutions' Feb 2026 production guide, the average non-technical founder spends 40-80 hours learning and implementing production hardening. The average technical founder (React/Node experience) spends 16-32 hours. The Production Lift trades €3,500 for 1 week of expert work instead of 2-8 weeks of learning + doing.
What if I only fix the critical items?
The four [CRITICAL] items (RLS, service_role exposure, password reset enumeration, webhook signatures) address the most common attack vectors. Fixing these alone closes 70% of the vulnerability surface. The [HIGH] items close the remaining 25%. The [MEDIUM] items are technical debt — they won't cause immediate breaches but will compound over time.
Is this checklist specific to Lovable?
The patterns apply to any AI-generated codebase: Bolt, v0, Cursor, Replit. The CVE-2025-48757 vulnerability was documented in Lovable specifically, but the same RLS gap exists in 44% of Bolt apps per VAS research. See Bolt to Production: The Fixed-Price Playbook for the Bolt-specific version.
What's the deliverable at the end of the Production Lift?
Your code, your repo, walk-away ownership. Full source transferred to your GitHub org. Vercel production deploy live. Sentry + Vercel Analytics wired. 30-day post-ship bug fix window. No platform lock-in, no recurring fees beyond your own infrastructure costs.
Ready to ship your Lovable prototype securely? The Production Lift is €3,500 fixed, 1 week. The same playbook that shipped wintura.ai — all 18 checklist items implemented, production-hardened, and deployed.
Related Articles
Bolt to Production: The €3,500 Fixed-Price Playbook
Ship your Bolt.new prototype to production in 1 week. Auth, multi-tenant RLS, Stripe webhooks, e2e tests — €3,500 fixed price.
5 Specific Patterns Where Bolt and Lovable Fail in Production — with the Production-Lift Fix
Real anti-patterns from Bolt/Lovable exports that fail when paying users arrive: app-layer tenancy, mock auth, missing webhook verification, generic error handlers, no a11y. Each with the production fix.
I Built Wintura.ai with Claude as My Pair-Programmer — Here's What AI Can and Can't Do in 2026
First-person breakdown of where Claude Sonnet 4.6 + Haiku 4.5 worked and where they failed across 6 months of shipping a production B2B SaaS solo. Real examples, not benchmarks.
Ready to build something great?
Architect-led, AI-accelerated. Let's turn your idea into a shipped product.