2026-07-14 · 7 min read · The Defen.so team

The vibe-coder security checklist

You built the whole app in a weekend. Cursor wrote most of it. Claude Code filled in the awkward parts. It works. You are now three days away from launching, and the security section of your notes still just says "add later."

This is the honest list of what breaks most on AI-generated web apps, in order of how often we see it. If you fix these ten things you are past the 95th percentile of shipped indie projects.

1. Your .env is public

The single most common finding. Someone committed .env to git, or the deploy target serves public/ instead of the app root, or the framework's default routing exposes hidden files. Test:

curl https://your-site.com/.env
curl https://your-site.com/.env.local
curl https://your-site.com/.env.production

If any of those return anything other than 404, even a redirect, you have a leak. Fix: block /.env* at the web server (nginx location ~ /\.env → 404) and rotate every secret in the file.

2. Your .git directory is downloadable

Same shape, worse blast radius. curl https://your-site.com/.git/config, if it returns a plain-text [core] block, an attacker can pull your entire git history including every deleted secret you ever committed. Fix: same nginx block, one line, does both.

3. Your Supabase RLS is off

Row Level Security disabled by default. If you generated a Supabase project and haven't manually enabled RLS on every table, any authenticated user (or in some cases any anonymous request with the anon key) can read every row. Test in the dashboard: Database → Tables → your table → RLS enabled?

Fix: turn it on for every table, then write policies. Start restrictive (deny all), then grant reads/writes explicitly per role.

4. Your Firebase rules are wide open

The default allow read, write: if true ships to production because the setup wizard warned you and you clicked through anyway. Attackers scan for open Firebase projects daily. Fix: publish rules that require request.auth != null plus a per-document owner check.

5. Your API keys are in the frontend bundle

OpenAI, Stripe, Twilio, Resend, if the key starts with sk_ or otherwise carries write permission, it should never appear in your JS bundle. Check: view-source your homepage, search for known prefixes. If you find one, the key is already leaked. Rotate immediately and proxy the calls through your backend.

6. Your login endpoint has no rate limit

An attacker can try 1,000 password combinations per second against your login route. Test with:

for i in {1..20}; do
  curl -so /dev/null -w "%{http_code}\n" -X POST https://your-site.com/login \
    -d "email=admin@example.com&password=wrong$i"
done

If all 20 return 200 (or the same "invalid credentials" HTML with no delay), you have no rate limit. Fix: put a limit like 5-per-IP-per-minute on /login and /register. Bonus: also limit password reset.

7. Your uploads accept anything

If you have a file-upload endpoint (avatars, CSV imports, whatever), test uploading a PHP file, then a polyglot (image that is also HTML), then a 10 GB file. If any succeeds, you have a problem. Fix: validate MIME by magic bytes not extension, cap size at the framework level, scan for polyglots.

8. Your HSTS is missing

Test: curl -sI https://your-site.com | grep -i strict-transport. If nothing prints, browsers will happily accept an http:// downgrade the first time a user hits your site. Fix: one HTTP header, one line of config, one restart.

9. Your query params are interpolated into SQL

You probably use an ORM and don't do this. But: search your codebase for raw(, DB::select(, ${req.query, + req.body, any pattern that concatenates user input into a query string. If it exists, an attacker will find it. Fix: parameterize every query.

10. You have no idea when you are being attacked

Your logs are somewhere. You have never looked at them. If someone starts probing your site right now, you will find out days later, from a customer complaint.

Fix: run npx @defen.so/init. Free tier gives you a real-time attack log for one site, an uptime monitor, and a weekly pentest, no credit card. When something matches an OWASP pattern, you get the URL, the payload, the IP, the ASN, and the verdict in your dashboard within seconds. It is not the point of this post to sell you anything but if you asked for the fastest way to close this loop, that is it.

The two-minute test

Fire the classics at your own site right now:

Ship. The AI wrote most of your code. It didn't do the security part. Ten items on this list, thirty minutes of work each on the hard ones, and you are done.

Related reading