Security checklist

AI-Generated Code Security Checklist

AI coding tools are excellent at building features. They are less reliable at enforcing security defaults. This checklist covers the 8 most common security gaps in AI-generated apps — with a focus on the patterns most often seen in Lovable, Bolt, v0, and Replit projects.

The 8-point checklist

Work through these in order — items 1 and 2 are the highest-severity findings in AI-generated apps and account for the most documented breaches.

  1. Supabase Row Level Security

    If your app uses Supabase (common in Lovable, Bolt, and v0 projects), every table in the public schema must have Row Level Security enabled with policies scoped to the authenticated user. The Supabase anon key ships in your client bundle — that is by design — but it grants data access according to your RLS policies. With RLS off or with a policy that allows SELECT to everyone, the anon key is a full read (sometimes write) credential.

    In May 2025, security researcher Matt Palmer disclosed CVE-2025-48757 (CVSS 9.3 Critical): insufficient RLS policies in Lovable-generated Supabase projects allowed unauthenticated attackers to read or write arbitrary database tables. This is not a theoretical risk — it is the documented failure mode for this class of app.

    How to check: In the Supabase dashboard, open Authentication → Policies. Every table should show "RLS enabled" and have at least one policy. For a definitive test, query the Supabase REST API with only the anon key and confirm you cannot read rows without authentication.

    Sources: CVE-2025-48757 on NVD. Original research: Matt Palmer's disclosure (mattpalmer.io). Supabase RLS: Supabase RLS docs.

  2. Secrets in the frontend bundle

    AI tools frequently place all configuration in .env files and then import everything in the frontend. In a Vite/React project, any variable prefixed VITE_ is baked into the built JS bundle and is visible to anyone who views source. The Supabase anon key is safe in the bundle (it is public by design). The Supabase service_role key, Stripe secret key (sk_live_ or sk_test_), or any third-party API token with write access is not — it must never reach the frontend.

    How to check: After building your project (npm run build), grep the output:

    grep -r "service_role|sk_live|sk_test|secret" dist/

    Any match that is not the anon key or a non-sensitive config value is a leak to fix.

    How to fix: Move secrets to a server-side function (Supabase Edge Function, Cloudflare Worker, Express route). Then rotate any secret that was in a previous build — it should be considered compromised.

  3. Security headers

    A freshly deployed Vite, Lovable, or Replit app typically serves no security headers. Missing Content-Security-Policy makes XSS easier to exploit. Missing Strict-Transport-Security allows HTTP downgrade on hostile networks. Missing X-Frame-Options enables clickjacking.

    How to check:

    curl -sI https://yourdomain.com | grep -i "content-security|x-frame|x-content|strict-transport|referrer"

    Zero output means none of these headers are set.

    The headers to add, in priority order: Content-Security-Policy, Strict-Transport-Security (HSTS), X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy.

    Set these at the hosting layer (Netlify _headers file, Vercel vercel.json headers block, nginx config) so they apply to every response from the app.

  4. CORS policy

    Access-Control-Allow-Origin: * on an API that reads the Authorization header or authenticates via cookies is a credential-theft risk in certain browser contexts. AI-generated backends often start with a permissive wildcard CORS policy and leave it there. Review what your API does with the Origin header and whether that policy is correct for what the API exposes.

    How to check: In your backend code, search for the CORS configuration. If you see origin: '*' and the same API handles authenticated requests, tighten it to an explicit allowlist of your frontend origins. A wildcard is fine for truly public, read-only, unauthenticated APIs — not for authenticated ones.

  5. Exposed .env and config files

    AI tools generate deployment configurations that often put the application directory and the web server document root in the same place. If the web server serves the project directory and .env is in it, the file is directly downloadable. This is one of the fastest ways to lose every credential in a project at once.

    Common paths that scanners — and attackers — check: /.env, /.env.local, /.env.production, /.env.backup, /.git/config.

    See the companion guide: How to Find Exposed .env Files on Your Website.

  6. Dependency pinning

    AI-generated package.json files almost always use caret ranges (^), which means npm install in production can pull a different, newer version of a dependency than you tested with. If that version contains a vulnerability — or is itself a supply-chain compromise — you get it automatically.

    How to check: Run npm audit and review the output. For production services, consider pinning exact versions in package-lock.json and committing the lock file.

    How to monitor ongoing: Enable Dependabot or npm audit in your CI pipeline to catch new advisories as they are published.

  7. Authentication flows

    Three specific weaknesses appear frequently in AI-generated auth implementations:

    • No rate limiting on login or password-reset endpoints. Without a limit, these routes are open to credential stuffing. Add rate limiting at the edge or API layer.
    • Password reset links that do not expire. A reset link left in someone's email indefinitely is an account-takeover risk. Set a short TTL (15–60 minutes).
    • Email enumeration via different error messages. If "user not found" and "wrong password" produce different error messages, an attacker can enumerate valid accounts. Return the same message for both cases.
  8. TLS configuration

    Many hosting platforms (Vercel, Netlify, Replit) handle TLS automatically and correctly. Self-hosted or VPS deployments can end up with weak cipher suites, an incomplete certificate chain, or no HSTS. Weak ciphers like RC4, DES, or 3DES are deprecated; any server offering them is vulnerable to known attacks.

    How to check: testssl.sh gives a comprehensive TLS analysis from the command line. pentes.io runs testssl.sh as part of every scan — the TLS section of the report covers certificate chain completeness, cipher suites, protocol versions, and HSTS.

    See also: Why Is My SSL Certificate Not Trusted? — the most common TLS misconfiguration explained.

For Lovable builders specifically

If you built with Lovable, the highest-priority items are 1 (Supabase RLS), 2 (secrets in the bundle), 3 (security headers), and 5 (exposed .env files). The Lovable-specific guide walks through each of these against the exact stack Lovable generates: Is My Lovable App Secure?

For all vibe coders

Whether you built with Lovable, Bolt, v0, Replit, or another AI tool, the attack surface is shaped by the same patterns: fast generation, configuration decisions made at AI speed without a security review step, and deployment to a real public domain with real users. The vibe coders hub covers the full picture — platform-specific guides, what a non-destructive scan covers, and the Vibe Coder plan that makes it affordable to scan every deploy.

How to run all of these checks at once

Some items on this checklist require manual inspection; others can be automated:

  • Automated by pentes.io scan: items 3 (security headers), 5 (exposed .env files), 8 (TLS configuration). The scan runs nuclei file-disclosure templates, a security-headers check, and testssl.sh — all non-destructive.
  • Manual or requires code access: items 1 (RLS — you must inspect Supabase policies), 2 (bundle secrets — you must grep your build output), 4 (CORS — you must read your backend config), 6 (dependencies — run npm audit), 7 (auth flows — you must test your endpoints).

A single pentes.io scan covers the public-surface checks in under five minutes. The manual items take longer but only need to be done once per significant architecture change.