← All posts
May 1, 2026checklistlovableboltcursorpre-launch

AI-Built App Security Checklist: 10 Checks Before You Launch

Don't ship your Lovable or Bolt app without these 10 security checks — RLS, exposed secrets, auth bypass, missing headers, and 6 more, each with a fix prompt.

AI tools make shipping fast. Security review usually gets skipped — not because developers don't care, but because there is no clear, AI-specific checklist to run through before going live.

This is that checklist. It covers the patterns that appear most frequently in independent audits of Lovable, Bolt, Cursor, and Claude-built apps. Run through it before you share your URL with anyone.

Skip the manual work: VibeScan runs items 1, 3, 7, 9, and parts of 2 and 6 automatically — paste your URL, get results in 30 seconds, no GitHub access needed.


1. No secret keys in client-side code

What to check: Open your deployed app in a browser. DevTools → Sources → search for sk_, SUPABASE_SERVICE, openai, stripe, database hostnames, and any other key prefixes you use.

What's safe: The Supabase anon key (prefixed NEXT_PUBLIC_) is designed to be public. Everything else — service role key, Stripe secret, OpenAI key, database connection strings — must be server-side only.

Why it matters: VibeScan's May 2026 early sample contained 14 critical findings, including live Stripe secrets and Twilio credentials visible in public JavaScript bundles.


2. Supabase RLS is on AND the policies are correct

What to check: Supabase Dashboard → Authentication → Policies. For every table with user data, read the USING clause of each SELECT policy.

Red flags:

USING (true)                         -- Open to everyone
USING (auth.role() = 'authenticated') -- Any logged-in user sees all rows

Correct pattern:

USING (auth.uid() = user_id)  -- User sees only their own rows

Why it matters: The dashboard can show RLS as enabled even when policies are permissive. Two apps in VibeScan's May 2026 early sample exposed Supabase tables to unauthenticated requests.


3. Security headers are configured

What to check: Run a URL scan with VibeScan, or use securityheaders.com on your deployed URL.

Minimum required:

// next.config.js headers()
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains" },

Why it matters: 53 of the first 61 completed VibeScan URL scans lacked the standard HTTP security-header set checked by the scanner. Missing headers leave browsers unable to enforce those protections.


4. Every route that reads user data verifies ownership server-side

What to check: For every API route that returns data, does the server verify that the requesting user owns that data — not just that they are logged in?

Red flag:

// Returns data for any authenticated user
const { data } = await supabase.from('notes').select('*');

Correct:

// Returns only the requesting user's data
const { data } = await supabase
  .from('notes')
  .select('*')
  .eq('user_id', session.user.id);

Why it matters: IDOR lets one user access another user's records by changing an identifier. The fix is server-side ownership verification, not frontend filtering. This check requires authenticated testing and is outside what a passive public-URL scan can prove.


5. Auth logic is not inverted

What to check: Find every route guard or middleware that checks authentication or role. Read the condition carefully.

Common AI-generated mistake:

// This blocks authenticated admins, allows anonymous users
if (user.role !== 'admin') {
  return new Response('Forbidden', { status: 403 });
}

Correct:

// This blocks non-admins
if (!user || user.role !== 'admin') {
  return new Response('Forbidden', { status: 403 });
}

Why it matters: Inverted auth logic can be syntactically valid and pass linting while doing the opposite of what the developer intended. Confirm it through source review and tests rather than relying on a scanner prevalence claim.


6. No admin or debug routes are publicly accessible

What to check: List every route in your app. Any route that performs admin actions, returns aggregate data, exposes internal state, or was built as scaffolding must require authentication.

Common patterns to look for: /admin, /debug, /internal, /api/admin, any route created during development that was not intended for public use.

Why it matters: A forgotten admin or debug route can expose internal state or privileged actions without authentication. VibeScan can look for public-surface symptoms, but authenticated authorization testing is still required.


7. Dependencies are not critically outdated

What to check: Run npm audit in your project. Or paste your package-lock.json or yarn.lock into VibeScan for CVE matching.

What to act on immediately: Any critical or high severity advisory with a known fix available. Not all vulnerabilities are exploitable in your specific usage, but anything critical with a simple npm update fix should be done before launch.

Why it matters: AI tools recommend packages based on training data that may be months or years old. Some of those packages have since had vulnerabilities disclosed.


8. Rate limiting is on for auth endpoints

What to check: Try hitting your sign-in, sign-up, and password reset endpoints in rapid succession. Does the server rate limit?

Supabase has built-in rate limiting on Auth endpoints — but if you have built custom auth flows or proxy endpoints, those need their own limits.

Why it matters: Missing rate limits enable credential stuffing and magic-link abuse. Verify the behavior against the limits documented by your auth provider and any custom proxy endpoints.


9. Email authentication is configured for your sending domain

What to check: Run the VibeScan email security checker on your domain. Confirm SPF and DMARC are present.

For Resend (the most common transactional email provider for AI-built apps):

  • SPF: add include:_spf.resend.com to your root domain TXT record
  • DKIM: add the CNAME record Resend provides
  • DMARC: add _dmarc TXT with at minimum v=DMARC1; p=quarantine

Why it matters: Without SPF and DMARC, your transactional emails (OTPs, receipts, password resets) are more likely to land in spam — and your domain can be spoofed to send phishing emails.


10. You have tested the app as a real user, not just as yourself

What to check: Create a second test account. Verify that the second account cannot access the first account's data by modifying IDs in URLs, API requests, or query parameters.

This takes five minutes and catches IDOR, open RLS, and auth bypass issues that automated scans sometimes miss because they require context about your specific data model.


Run the automated checks

VibeScan covers items 1, 3, 7, 9, and parts of 2 and 6 automatically in a 30-second scan. Paste your URL and get results without connecting your repository.

Items 4, 5, 8, and 10 require manual review of your code and app behavior — they involve semantic analysis that no automated scanner fully handles yet.


Sources: PreBreach OWASP Top 10 in AI-Generated Code (Feb 2026); ShipSafe Cursor Security Analysis (Mar 2026); VibeWrench 100-app benchmark (2025–2026); CVE-2025-48757 (Lovable).

Check your own app

Free scan — no GitHub access needed. Takes 30 seconds.

Scan my app free