AI for Security Code Review — Find Vulnerabilities Before They Ship

Sanjeev SharmaSanjeev Sharma
6 min read

Advertisement

Introduction

Why This Matters

Security vulnerabilities are expensive to fix after deployment and catastrophic after exploitation. Most development teams lack dedicated security engineers on every PR review cycle. AI tools fill the gap by providing a fast, automated first pass that catches the most common vulnerability classes — SQL injection, XSS, insecure deserialization, hardcoded secrets, and authentication mistakes — before code ships. AI security review is not a replacement for a professional penetration test, but it is a reliable first layer that catches issues that should never make it to production.

What AI Security Review Catches Reliably

AI models trained on large codebases recognize the fingerprints of common vulnerabilities accurately:

High reliability (catches consistently):

  • SQL injection from string concatenation
  • Hardcoded API keys, passwords, or tokens
  • Missing input validation before database writes
  • Insecure direct object references (IDOR) — accessing data without ownership check
  • Missing HTTPS enforcement
  • Weak cryptography (MD5, SHA1 for passwords)
  • Debug routes or endpoints left in production code

Moderate reliability (catches often, may miss edge cases):

  • XSS from unescaped user input in HTML templates
  • CSRF missing token verification
  • Insecure session configuration
  • Missing rate limiting on authentication endpoints
  • Overly permissive CORS configuration

Low reliability (requires human expertise):

  • Business logic vulnerabilities specific to your domain
  • Complex authorization bypass scenarios
  • Race conditions in security-critical paths
  • Cryptographic protocol design flaws

SQL Injection Detection

Vulnerable code:

# Never do this
def get_user(username: str) -> dict:
    query = f"SELECT * FROM users WHERE username = '{username}'"
    return db.execute(query).fetchone()

Prompt: "Review this code for security vulnerabilities."

AI response: "This code is vulnerable to SQL injection. If username contains ' OR '1'='1, the query becomes SELECT * FROM users WHERE username = '' OR '1'='1', which returns all users. Use parameterized queries instead."

Fixed code:

def get_user(username: str) -> dict:
    query = "SELECT * FROM users WHERE username = %s"
    return db.execute(query, (username,)).fetchone()

Authentication and Session Security

// Review this authentication endpoint
app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await db.query(
    `SELECT * FROM users WHERE email = '${email}' AND password = '${password}'`
  );
  if (user) {
    req.session.userId = user.id;
    res.json({ success: true });
  } else {
    res.json({ success: false });
  }
});

AI findings on this code:

  1. SQL injection: String interpolation in the query — use parameterized queries
  2. Plaintext password comparison: Passwords should be hashed with bcrypt or argon2; never stored or compared as plaintext
  3. No rate limiting: This endpoint can be brute-forced without restriction
  4. Information leakage in error response: { success: false } reveals that the login failed; consider a generic error message
  5. Missing CSRF protection: POST endpoint modifying session state needs CSRF token verification

Secure version:

const bcrypt = require('bcrypt');
const rateLimit = require('express-rate-limit');
 
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 10, // 10 attempts per window
  message: 'Too many login attempts. Try again later.'
});
 
app.post('/login', loginLimiter, async (req, res) => {
  const { email, password } = req.body;
  
  // Parameterized query
  const user = await db.query(
    'SELECT id, password_hash FROM users WHERE email = $1',
    [email]
  );
  
  // Constant-time password comparison
  const valid = user && await bcrypt.compare(password, user.password_hash);
  
  if (valid) {
    req.session.regenerate(() => {  // Prevent session fixation
      req.session.userId = user.id;
      res.json({ success: true });
    });
  } else {
    // Same response for wrong email or wrong password (prevents enumeration)
    res.status(401).json({ error: 'Invalid credentials' });
  }
});

Hardcoded Secret Detection

AI reliably spots hardcoded credentials:

# AI flags this immediately
DATABASE_URL = "postgresql://admin:supersecret123@prod-db.internal/myapp"
STRIPE_SECRET_KEY = "sk_live_abc123xyz..."
JWT_SECRET = "my-very-secret-key"

AI response: "This code contains hardcoded credentials and secrets. These should be loaded from environment variables, never committed to source control. Use os.environ.get('DATABASE_URL') and a secrets manager (AWS Secrets Manager, HashiCorp Vault) for production."

Insecure Direct Object Reference (IDOR)

# Vulnerable: no ownership check
@app.get("/api/invoices/{invoice_id}")
async def get_invoice(invoice_id: int, current_user: User = Depends(get_current_user)):
    invoice = db.query(Invoice).filter(Invoice.id == invoice_id).first()
    if not invoice:
        raise HTTPException(status_code=404)
    return invoice  # Returns invoice even if it belongs to another user

AI finding: "This endpoint is vulnerable to IDOR. Any authenticated user can access any invoice by guessing or enumerating invoice_id. Add an ownership check: filter(Invoice.id == invoice_id, Invoice.user_id == current_user.id)."

Building a Security Review Prompt

A generic "review for security" prompt produces generic output. Be specific:

Review this code for the following security concerns:
1. SQL injection and NoSQL injection
2. Authentication and authorization bypass
3. Hardcoded secrets or credentials
4. Missing input validation
5. Insecure session management
6. Information leakage in error messages
7. Missing rate limiting on sensitive endpoints
 
For each issue found, explain: (a) the vulnerability, (b) how it could be
exploited, and (c) the fix.

Common Mistakes

  • Treating AI security review as sufficient: AI catches common patterns, not domain-specific business logic vulnerabilities. Use it as a first layer, not the only layer.
  • Reviewing only the diff, not the context: Vulnerabilities often span multiple files. Review the full request/response flow, not just the changed lines.
  • Not verifying AI-suggested fixes: AI-generated security fixes are usually correct but should be tested and reviewed — sometimes they introduce new issues.
  • Skipping security review for "small" changes: Many significant vulnerabilities were introduced in small, seemingly innocuous changes.

Best Practices

  • Run AI security review on every PR that touches authentication, authorization, data access, or user input handling
  • Provide the full request handling chain — middleware, route handler, database layer — not just the endpoint function
  • Ask the AI to identify the worst-case exploitation scenario for each finding, not just name the vulnerability class
  • Use AI security review as a pre-check before a professional penetration test, not as a replacement for it
  • Add flagged patterns to your team's code review checklist so humans catch them too

Key Takeaways

  • AI reliably detects SQL injection from string concatenation, hardcoded secrets, missing input validation, and weak password hashing
  • The most effective security review prompt lists specific vulnerability classes and asks for exploitation scenarios, not just issue names
  • IDOR (Insecure Direct Object Reference) is one of the most common vulnerabilities in API endpoints and AI catches it consistently
  • Authentication endpoints need rate limiting, CSRF protection, constant-time comparison, and session regeneration — AI checks all four
  • AI security review is a fast first layer, not a replacement for a professional penetration test or dedicated security engineer review
  • Never paste production code containing real credentials, PII, or customer data into a cloud AI service
  • Small changes introduce vulnerabilities as often as large ones — apply security review consistently, not selectively
  • Ask AI to suggest the fix and explain why the fix works — understanding the root cause prevents recurrence

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading