Security Checklist for AI-Generated Code
Quick-reference security checklist. Run through this before shipping any AI-generated code to production.
security checklist production vulnerabilities
AI writes fast. You make it safe. Run through this checklist before every deploy.
Companion to the full Security for Vibecoders guide.
🔑 Secrets & Configuration
| Check | Why | How to Fix |
|---|---|---|
| No hardcoded passwords, API keys, or tokens in code | Bots scrape GitHub for secrets within minutes of pushing | Move to .env file + process.env / os.environ |
.env is in .gitignore | Prevents accidentally committing secrets | Add .env and .env.* to .gitignore |
| No secrets in frontend/client-side code | Browser code is visible to everyone | Move to server-side API routes; use NEXT_PUBLIC_ only for non-secret values |
| Different secrets for dev/staging/production | Compromise in dev shouldn’t affect production | Use separate .env files or environment-specific config |
Run gitleaks detect before pushing | Catches any secrets that slipped through | Install gitleaks, add as pre-commit hook |
🔐 Authentication & Authorization
| Check | Why | How to Fix |
|---|---|---|
| Passwords hashed with bcrypt or argon2 | Plain text/MD5/SHA passwords are trivially cracked | bcrypt.hash(password, 12) — never store plain text |
| JWT tokens have expiration | Tokens without expiry are permanent access keys | jwt.sign(payload, secret, { expiresIn: '24h' }) |
| Auth checked on server, not just client | Client-side checks are trivially bypassed | Add middleware to every protected API route |
| Rate limiting on login/signup | Prevents brute force attacks | Use express-rate-limit or similar: 5 attempts/min |
| Session invalidation on password change | Old sessions should die when password changes | Clear all sessions/tokens for user on password update |
| No custom auth crypto | Roll-your-own auth is always broken | Use NextAuth, Passport.js, Auth0, Supabase Auth, Clerk |
✅ Input Validation
| Check | Why | How to Fix |
|---|---|---|
| All input validated on the server | Client validation is UX, server validation is security | Use Zod, Joi, or Yup on every API endpoint |
| SQL queries use parameterized statements | String concatenation = SQL injection | Use $1 params or ORM (Prisma, Drizzle) |
| HTML output escaped | Unescaped user input = XSS attacks | Use framework defaults (React auto-escapes); sanitize with DOMPurify if using dangerouslySetInnerHTML |
| File uploads validated | Malicious files can execute code on server | Check file type (magic bytes, not just extension), enforce size limits, store outside webroot |
| URL redirects use allowlist | Open redirects enable phishing | Only redirect to known paths: if (allowedUrls.includes(url)) |
| Request body size limited | Large payloads can crash your server | app.use(express.json({ limit: '1mb' })) |
📦 Dependencies
| Check | Why | How to Fix |
|---|---|---|
npm audit / pip audit clean | Known vulnerabilities in your dependency tree | Run npm audit fix or update vulnerable packages |
| Lock file committed | Ensures consistent builds; prevents supply chain attacks | Commit package-lock.json / poetry.lock / pnpm-lock.yaml |
| No unnecessary packages | Each dependency is an attack surface | Remove unused packages; prefer built-in APIs |
| Check for typosquatting | Attackers publish expres instead of express | Verify package names, check download counts and publisher |
| Dependabot or Renovate enabled | Automated alerts for vulnerable dependencies | Enable in GitHub repo settings (free) |
🚀 Deployment
| Check | Why | How to Fix |
|---|---|---|
| HTTPS enforced | HTTP traffic is readable by anyone on the network | Use platform SSL (Vercel, Railway auto-enable) or Let’s Encrypt |
| Security headers set | Prevents clickjacking, XSS, MIME sniffing | Add CSP, HSTS, X-Frame-Options, X-Content-Type-Options |
| Debug mode OFF | Debug mode exposes stack traces, source maps, internal state | Ensure NODE_ENV=production; remove DEBUG=* |
| Error messages are generic | Detailed errors help attackers understand your system | Return “Something went wrong” to users; log details server-side |
| Database not publicly accessible | Public DB = anyone can connect | Use private networking; require SSH tunnel or VPN for access |
| CORS restricted to your domains | origin: '*' lets any site make requests to your API | Allowlist your specific domains |
| Admin routes protected | Exposed admin panels are the #1 way apps get compromised | Auth + IP restriction + separate subdomain |
🏃 Quick Commands
# Scan for secrets
gitleaks detect
trufflehog git file://.
# Audit dependencies
npm audit
pip audit
# Security linting
npx eslint --plugin security .
semgrep --config=p/owasp-top-ten .
# Check security headers (after deploy)
curl -I https://yoursite.com | grep -i "security\|strict\|x-frame\|x-content\|csp"
🤖 Security Prompt Templates
Before generating code:
“Use environment variables for all secrets. Validate all input with Zod. Use parameterized queries. Add rate limiting to auth endpoints.”
After generating code:
“Review this code for OWASP Top 10 vulnerabilities. Check for: SQL injection, XSS, hardcoded secrets, missing auth, insecure defaults.”
Before deploying:
“Generate security headers middleware for Express/Next.js. Include: CSP, HSTS, X-Frame-Options, X-Content-Type-Options. Restrict CORS to [my domain].”
✓ Copied to clipboard!