Why This Matters
Fixing a security vulnerability in production costs roughly 100× what it costs to fix during the coding phase. Every minute you spend on this checklist is worth an hour of incident response, lawyer calls, and customer-trust rebuild later.
The Problem
AI generates a login endpoint. Looks great. Runs perfectly. Ships to production. Three days later: your database is leaked. The SQL injection was obvious in hindsight, but AI treated user input like trusted data.
The core issue: AI learns from code examples that include security vulnerabilities. Stack Overflow, GitHub, tutorials — many contain insecure patterns. AI statistically reproduces these. It doesn't understand threat models, it generates "typical" code. Typical code is often vulnerable code.
You can't catch these by eyeballing the diff. Without a structured pass, you'll miss the category you didn't think to look for — and AI's confident tone makes the gap invisible.
The Core Insight
Use the OWASP Top 10 as a systematic checklist, not a reading list. The Top 10 isn't trivia — it's a structured taxonomy of how web applications get breached. Walk every AI-generated diff through each category in order. Skip the intuition; trust the framework.
The categories cover what an attacker tries first: broken access control, injection, cryptographic failures, insecure design, security misconfiguration. If your code passes a deliberate sweep across all ten, you've covered ~90% of real-world attack surface. The other 10% lives in language-specific footguns (see the sibling guide on language vulnerabilities).
Think like a penetration tester: every input is malicious, every database query is a potential injection point, every API call is a chance for exploitation. The checklist below is your scoring rubric.
The False Confidence Trap
AI explains its own code with the same confidence whether the code is secure or full of holes. "Looks good" from the model is not a signal — it's noise. This is the input side of the same problem described in AI's Validation Blindspot below: the model's confidence in what it wrote is uncorrelated with whether the code actually handles adversarial inputs. That's exactly why you need the OWASP checklist instead of a "does this look secure?" gut check.
AI's Validation Blindspot
AI almost always validates "happy path" inputs (email format, length limits) but misses adversarial inputs (SQL keywords, XSS payloads, prototype-pollution keys, NoSQL operator objects). The model has been trained on what valid data looks like, not what attackers send. Always test with OWASP attack strings — this is the output side of The False Confidence Trap. The model can't see what it doesn't think to validate, and "I added input validation" never means "I added adversarial input validation."
OWASP Top 10: The Checklist
What follows is the OWASP Top 10 (2021 edition) reorganized as a review pass you can run in 30 minutes. Each category lists what to verify, with the AI-specific failure modes called out. Work top-to-bottom; do not skip categories because "we don't do that here."
A03 — Injection & Input Validation
| Check | What to Verify | Risk if Missing |
|---|---|---|
| User Input Validation | All inputs validated against schema/regex | XSS, SQL Injection, Command Injection |
| Type Checking | Inputs coerced to expected types | Type confusion attacks |
| Length Limits | Maximum lengths enforced | DoS, buffer overflow |
| Whitelist vs Blacklist | Uses whitelist of allowed values | Bypass via unexpected inputs |
| File Upload Validation | File type, size, content verified | Malicious file execution |
Code Review Questions:
- Is user input validated BEFORE processing?
- Are validation errors returned safely (no stack traces)?
- Is file content checked, not just extension?
- Are uploaded files stored outside webroot?
A01 & A07 — Broken Access Control + Authentication Failures
Authentication Checks:
- Password Storage: Using bcrypt/argon2, not MD5/SHA1?
- Session Management: Secure, HTTP-only cookies? CSRF tokens?
- Token Expiry: JWTs have expiration? Refresh token rotation?
- Brute Force Protection: Rate limiting on login endpoints?
- Password Reset: Tokens time-limited and single-use?
Authorization Checks:
- Resource Access: Every endpoint checks user permissions?
- IDOR Prevention: Can't access others' data by changing IDs?
- Role Checks: Admin functions restricted to admin role?
- API Keys: Scoped to minimum necessary permissions?
# Bad: AI often generates
app.get('/user/:id', async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user); // Anyone can view any user!
});
# Good: Authorization check
app.get('/user/:id', authMiddleware, async (req, res) => {
const requestedId = req.params.id;
const currentUser = req.user;
// Users can only view their own data unless admin
if (requestedId !== currentUser.id && !currentUser.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
const user = await User.findById(requestedId);
res.json(user);
});
A02 — Cryptographic Failures & Data Protection
Encryption Checks:
- Data in Transit: HTTPS enforced, not just available?
- Data at Rest: Sensitive fields encrypted in database?
- Encryption Algorithms: Using AES-256, not DES/3DES?
- Key Management: Keys stored in secrets manager, not code?
Data Exposure Checks:
- Error Messages: Don't leak stack traces to users?
- Logging: Don't log passwords, tokens, credit cards?
- API Responses: Don't return more data than necessary?
- Debug Mode: Disabled in production?
A03 (continued) — Server-Side Injection Vectors
Generic injection categories appear in every language. For the language-specific footguns (Python pickle, JS prototype pollution, Go race conditions), see the sibling guide on common AI vulnerabilities by language.
SQL Injection Prevention:
# Bad: String concatenation
query = f"SELECT * FROM users WHERE email = '{email}'"
# Good: Parameterized queries
query = "SELECT * FROM users WHERE email = ?"
db.execute(query, [email])
Other Injection Types:
- Command Injection: Never pass user input to shell commands
- LDAP Injection: Sanitize LDAP queries
- XML Injection: Disable external entities in XML parsers
- Template Injection: Don't eval() or render() user content
A03 (client-side) — Cross-Site Scripting (XSS)
XSS Prevention Checklist:
- User content HTML-escaped before rendering?
- Content-Security-Policy header set?
- Using safe DOM methods (textContent, not innerHTML)?
- Rich text input sanitized (DOMPurify, etc.)?
- User-uploaded HTML sandboxed?
// Bad: Direct injection
element.innerHTML = userInput;
// Good: Escaped rendering
element.textContent = userInput;
// If HTML needed: Sanitize first
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);
A04 & A05 — Insecure Design + Security Misconfiguration (API surface)
API-Specific Checks:
- Rate Limiting: Prevent abuse via excessive requests?
- CORS: Restricted to known origins, not wildcard?
- API Versioning: Backward compatibility maintained?
- Mass Assignment: Only allowed fields updateable?
- Pagination: Enforced on list endpoints?
The Mass Assignment Trap
# Bad: AI often generates
app.patch('/user/:id', async (req, res) => {
await User.update(req.params.id, req.body);
// User can update ANY field, including isAdmin!
});
# Good: Explicit field allowlist
app.patch('/user/:id', async (req, res) => {
const allowedFields = ['name', 'email', 'bio'];
const updates = {};
for (const field of allowedFields) {
if (req.body[field] !== undefined) {
updates[field] = req.body[field];
}
}
await User.update(req.params.id, updates);
});
A06 & A08 — Vulnerable Components + Software Integrity
Third-Party Code Checks:
- Dependencies up-to-date (no known CVEs)?
- Using
npm audit/pip-audit/cargo audit? - Dependencies from trusted sources only?
- Lock files committed (package-lock.json, Pipfile.lock)?
- Unnecessary dependencies removed?
A09 & A10 — Logging Failures + Server-Side Request Forgery
Security Logging Checks (A09):
- Auth events (login success/failure, password change, MFA) logged with user ID + IP?
- Failed access-control checks logged so abuse is detectable?
- Logs free of secrets, tokens, full credit-card numbers?
- Log retention long enough for forensic response (90+ days for auth)?
SSRF Checks (A10):
- Any endpoint that fetches a user-supplied URL validates the host against an allowlist?
- Cloud metadata IPs (169.254.169.254 and friends) explicitly blocked?
- Redirects followed by your HTTP client are bounded and re-validated?
The Walkthrough: Security Audit Process
Step 1: Automated Scanning (5 minutes)
# Run security linters
npm audit
npm run lint:security # ESLint security plugin
snyk test
# Static analysis
semgrep --config=auto
# Dependency check
npm outdated
Step 2: Manual Code Review (15-30 minutes)
Go through the checklist above, focusing on:
- All user input entry points
- All database queries
- All authentication/authorization logic
- All API endpoints
Step 3: Test with Attack Vectors (10 minutes)
# SQL Injection test
curl -X POST /login -d "email=admin'--&password=x"
# XSS test
curl -X POST /comment -d "text="
# IDOR test
# Login as user A, try to access user B's resources
# Rate limit test
# Send 100 requests in 1 second, should be blocked
Failure Patterns
1. Trust AI Without Verification
Symptom: AI said "secure," so you ship it.
Fix: Run automated scanners. AI doesn't know your threat model.
2. Fixing Symptoms, Not Root Cause
Symptom: You escape one XSS payload, but miss the pattern.
Fix: If one SQL injection exists, audit ALL queries. Patterns repeat.
3. Security Theater
Symptom: You validate email format but allow SQL keywords.
Fix: Think like an attacker. What's the actual risk?
Quick Reference
30-Second Security Scan:
- Search code for:
eval(,.innerHTML,exec(,System( - Check: Are passwords hashed? Are queries parameterized?
- Test: Can you access another user's data by changing an ID?
Critical Security Rules:
- Never trust user input (validate everything)
- Use parameterized queries (never string concatenation)
- Check authorization on every endpoint
- Hash passwords (bcrypt with 12+ rounds)
- Escape output (prevent XSS)
- Rate limit authentication endpoints
- Keep dependencies updated
AI Security Audit Prompt:
"Review this code for security vulnerabilities:
1. SQL injection via string concatenation
2. XSS via unsanitized rendering
3. Missing authentication/authorization checks
4. Secrets hardcoded in source
5. Unsafe deserialization
6. Command injection risks
7. Timing attacks in authentication
List every vulnerability found with severity."
Fast Fail Criteria (Reject Immediately):
- Hardcoded passwords/API keys
- SQL queries via string concatenation
- No authentication on sensitive endpoints
- User input directly eval()'d or exec()'d
- Passwords stored in plaintext
Key Takeaways
What To Remember
- OWASP Top 10 is your checklist, not a reading list. Walk every AI-generated diff through all ten categories — the gap is always in the category you skipped.
- AI confidence is noise. The False Confidence Trap and the Validation Blindspot mean "looks good" from the model tells you nothing about whether the code handles adversarial input.
- Fix during coding (1×), not in production (100×). The cost curve is the whole reason this pass is worth 30 minutes.
- Three-step audit. Automated scanners (5 min) → manual OWASP walkthrough (15-30 min) → live attack-vector tests (10 min). Skip any step and you've left a category uncovered.
- If one injection exists, audit all queries. AI failure modes repeat across the codebase — one finding is a pattern, not an exception.
Related Guides
This guide handles the generic OWASP categories. The two sibling guides in this module go deeper on adjacent angles:
- Common AI Vulnerabilities by Language — the language-specific footguns OWASP categories don't capture (Python pickle, JS prototype pollution, Go races, Rust panics).
- Shift-Left Security with AI Assistance — how to run this checklist during coding instead of after the feature is built.