You built your MVP with Claude, Cursor, Lovable, or Copilot. It works. It's fast. It ships.

Then reality hits: 196 out of 198 AI-generated apps contain security vulnerabilities (Endor Labs, 2025). Not "might contain." Contain.

Here's the uncomfortable truth: AI coding assistants are trained on decades of vulnerable code from GitHub, Stack Overflow, and public repositories. They replicate those patterns at scale—not out of malice, but because they're statistically likely. A model that generated perfectly secure code 100% of the time would have to unlearn everything it learned during training.

This isn't a flaw in AI. It's a gap in how we use it.

Below are the 10 most dangerous security flaws AI tools introduce into production code, why they happen, and how to fix them.

45%
of AI-generated code contains security vulnerabilities (Veracode, 2025)
62%
of AI solutions have design flaws or known vulnerabilities (Cloud Security Alliance, 2025)
70%
of AI-generated Java code fails security tests (Checkmarx)
35
new CVEs from AI-generated code in March 2026 alone (Georgia Tech)
1
Missing Input Validation (CWE-20)

The most common flaw, and the easiest to miss. AI-generated endpoints often accept and trust anything the client sends.

❌ AI-Generated — Vulnerable
// AI-generated API endpoint
app.post('/api/users', (req, res) => {
  const user = {
    email: req.body.email,
    role: req.body.role,     // ← AI accepts whatever role the request sends
    isAdmin: req.body.isAdmin  // ← Should NEVER come from user input
  };
  db.insert('users', user);
  res.json({ success: true });
});
Why AI generates it: Most example code shows simple request→database flows without validation. AI predicts the shortest path to a "working" solution. Security guardrails require context the model doesn't have without explicit prompting.
40% of AI-generated code solutions omit input validation entirely — Endor Labs study
✓ Fixed Version
app.post('/api/users', (req, res) => {
  // Validate email format
  if (!req.body.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(req.body.email)) {
    return res.status(400).json({ error: 'Invalid email' });
  }

  // Whitelist allowed roles
  const allowedRoles = ['user', 'moderator'];
  if (!allowedRoles.includes(req.body.role)) {
    return res.status(400).json({ error: 'Invalid role' });
  }

  // Never allow isAdmin from user input
  const user = {
    email: req.body.email,
    role: req.body.role,
    isAdmin: false  // ← Always set server-side
  };
  db.insert('users', user);
  res.json({ success: true });
});
2
SQL Injection (CWE-89)

The classic vulnerability, still alive and thriving in AI-generated code. String interpolation in queries is an open door for attackers.

❌ AI-Generated — Vulnerable
const userId = req.query.id;
const query = `SELECT * FROM users WHERE id = ${userId}`;
db.query(query);
// Attacker sends: id=1 OR 1=1 — returns entire users table
// Or: id=1; DROP TABLE users; — deletes everything
Why AI generates it: String concatenation is the most frequent pattern in tutorial code. Template literals look simpler than parameterized queries. AI picks the pattern it sees most.
✓ Fixed Version
// Use parameterized queries — ALWAYS
const userId = req.query.id;
const query = 'SELECT * FROM users WHERE id = $1';
db.query(query, [userId]);  // Parameter passed separately, never interpolated
3
Hardcoded Secrets (CWE-798)

One of the most dangerous flaws. API keys, passwords, and tokens embedded directly in source code get committed to Git — and attackers scan GitHub 24/7 looking for exactly this.

❌ AI-Generated — Vulnerable
const API_KEY = "sk-proj-abc123xyz789def456";  // ← In source code!
const DB_PASSWORD = "prod_password_2024";

const stripe = require('stripe')(API_KEY);
const pool = new Pool({ password: DB_PASSWORD });
35 CVEs in March 2026 alone involved stolen API keys from AI-generated code. CVE-2026-21852 specifically targeted Claude Code loading .env files silently into memory.
✓ Fixed Version
// Use environment variables — always
const API_KEY = process.env.STRIPE_API_KEY;
const DB_PASSWORD = process.env.DATABASE_PASSWORD;

if (!API_KEY || !DB_PASSWORD) {
  throw new Error('Missing required environment variables');
}

const stripe = require('stripe')(API_KEY);
const pool = new Pool({ password: DB_PASSWORD });
And add to .gitignore:
.env
.env.local
.env.*.local
4
Broken Authentication (CWE-287)

AI-generated auth checks are often superficial — checking that a field exists, not that it's actually valid.

❌ AI-Generated — Vulnerable
app.get('/api/dashboard', (req, res) => {
  // Just checks if a username was provided — no password, no token
  if (req.body.username) {
    res.json({ dashboardData: getAllData() });
  } else {
    res.status(401).json({ error: 'Unauthorized' });
  }
});
Why AI generates it: Authentication flows are complex and vary by framework. Many tutorials show incomplete examples. AI generates "good enough" auth checks without understanding that weak verification = no verification.
✓ Fixed Version
app.get('/api/dashboard', authenticateToken, (req, res) => {
  const user = req.user;  // Set by middleware after real token verification
  res.json({ dashboardData: getUserData(user.id) });
});

function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) return res.status(401).json({ error: 'No token' });

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.status(403).json({ error: 'Invalid token' });
    req.user = user;
    next();
  });
}
5
Broken Access Control (CWE-639)

Even when authentication works, authorization is often missing. Any logged-in user can view any other user's data just by changing an ID in the URL.

❌ AI-Generated — Vulnerable
app.get('/api/users/:id/profile', (req, res) => {
  const userId = req.params.id;
  const userProfile = db.query('SELECT * FROM users WHERE id = ?', [userId]);
  res.json(userProfile);
  // ← Returns ANY user's profile to ANY logged-in user
  // Visit /api/users/1/profile to get admin's data
});
Why AI generates it: The AI sees the URL parameter and returns it. It doesn't automatically reason: "Should I check if the requesting user is authorized to see this?" That's an architectural assumption the AI doesn't have.
✓ Fixed Version
app.get('/api/users/:id/profile', authenticateToken, (req, res) => {
  const requestedUserId = req.params.id;
  const currentUserId = req.user.id;  // From verified JWT

  // Only allow users to access their own profile (or admins)
  if (requestedUserId !== currentUserId && req.user.role !== 'admin') {
    return res.status(403).json({ error: 'Forbidden' });
  }

  const userProfile = db.query('SELECT * FROM users WHERE id = ?', [requestedUserId]);
  res.json(userProfile);
});
6
Cross-Site Scripting — XSS (CWE-79)

Injecting malicious scripts into your pages through user-generated content. React protects you most of the time — but edge cases break this assumption.

❌ Vulnerable — Unescaped HTML
// If comment.text = "<img src=x onerror='stealCookie()'>"
// The script runs in every visitor's browser
function CommentDisplay({ comment }) {
  return <div dangerouslySetInnerHTML={{ __html: comment.text }} />;
}
✓ Fixed Version
import DOMPurify from 'dompurify';

function CommentDisplay({ comment }) {
  // Sanitize HTML before rendering — removes script tags and event handlers
  return <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment.text) }} />;
}

// Or if you don't need HTML formatting at all:
function CommentDisplay({ comment }) {
  return <div>{comment.text}</div>;  // React auto-escapes this
}
7
Insecure Cryptography (CWE-327)

AI-generated password hashing frequently uses MD5 or SHA-1 — algorithms that were broken over 20 years ago. Some AI code stores passwords in plaintext entirely.

❌ AI-Generated — Vulnerable
// Using weak/deprecated algorithms
const hashedPassword = crypto.createHash('md5').update(password).digest('hex');
// MD5 has been broken since 2004. Rainbow tables crack these instantly.

// Or even worse — plaintext:
db.insert('users', { email, password: plaintext_password });
Why AI generates it: MD5 is the most common example in old tutorials. The AI doesn't know it's broken — it just knows it appears frequently in code examples.
✓ Fixed Version
const bcrypt = require('bcrypt');

// Hash before storing — bcrypt is slow by design, making brute-force impractical
const hashedPassword = await bcrypt.hash(password, 12);  // 12 = work factor
db.insert('users', { email, password: hashedPassword });

// When verifying login:
const isValid = await bcrypt.compare(providedPassword, storedHash);
8
Exposed Configuration Files

AI tools generate project scaffolding that often includes .env files, config files with hardcoded values, or .claude directories — and none of it gets added to .gitignore.

❌ Common Repository Mistake
# These files committed to public GitHub repos:
/.env              # Database URLs, API keys, JWT secrets
/.claude           # Claude Code config — may contain credentials
/config.js         # Hardcoded database URLs
/scripts/seed.js   # Contains production database connection strings
CVE-2025-59536: Claude Code's MCP and Hooks features allowed remote code execution via malicious config files (CVSS 8.7).
CVE-2026-21852: Attackers could steal API keys by injecting malicious repository configuration.
✓ Always Use a .gitignore
# .gitignore — add these before first commit
.env
.env.local
.env.*.local
.env.production
config.local.js
*.pem
*.key
secrets/
.claude/
9
Insecure Dependencies (CWE-1104)

AI generates npm install commands based on tutorials — often referencing packages that are years out of date with known CVEs. The code works. The security holes are invisible.

❌ What AI Often Installs
npm install some-popular-library@1.0.0
# That version from 5 years ago? It has 12 known CVEs.
# AI doesn't check vulnerability databases — it just patterns match.
✓ Run These Regularly
# Check for vulnerabilities
npm audit

# Auto-fix what it can
npm audit fix

# See what's outdated
npm outdated

# Fail CI/CD builds if high-severity vulns exist
npm audit --audit-level=high
10
Insufficient Logging & Monitoring (CWE-778)

The invisible flaw. When there's no logging, attacks go undetected until after the breach. AI-generated code generates minimal working code — which means zero audit trail.

❌ AI-Generated — No Visibility
app.post('/api/login', (req, res) => {
  const user = db.query('SELECT * FROM users WHERE email = ?', [req.body.email]);
  if (user && passwordMatches) {
    res.json({ token: generateJWT(user) });
  } else {
    res.status(401).json({ error: 'Bad credentials' });
  }
  // No logging. Brute-force attack against your app? You'll never know.
});
✓ Fixed Version — With Audit Trail
app.post('/api/login', (req, res) => {
  const email = req.body.email;
  const user = db.query('SELECT * FROM users WHERE email = ?', [email]);

  if (!user) {
    logger.warn(`Login attempt for non-existent user: ${email} from ${req.ip}`);
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  if (!passwordMatches) {
    logger.warn(`Failed login for ${email} from ${req.ip}`);
    incrementFailedLoginCount(email);  // Trigger lockout after N attempts
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  logger.info(`Successful login: ${email} from ${req.ip}`);
  res.json({ token: generateJWT(user) });
});

The Bigger Picture: Why This Keeps Happening

AI makes code faster. It doesn't make code safer.

Think of it like a contractor who builds fast and cuts corners. The house stands. The doors open. But the locks are cheap. You have to inspect the work before you move in.

These ten flaws aren't rare edge cases from obscure models. They appear consistently across Cursor, Copilot, Claude, ChatGPT, and Lovable — because they all learned from the same vulnerable codebase: the internet.

How to Protect Your AI-Generated Code

🔍

1. Manual Review Before Deploy

AI-generated code in security-critical paths (auth, payments, data access) requires human eyes. Specifically look for the 10 flaws above. Treat it the same way you'd treat code from a new contractor.

2. Static Analysis (SAST)

Run automated scanners on your code before it ships. SonarQube finds security smells. Snyk catches vulnerable dependencies. Checkmarx specializes in injection flaws. Most have free tiers.

🎯

3. Dynamic Testing (DAST)

Attack your own running application. Tools like OWASP ZAP and Burp Suite simulate real attacks against your staging environment — SQL injection, XSS, auth bypasses. Find them before attackers do.

📦

4. Dependency Scanning (SCA)

Run npm audit before every deploy. Pin major dependency versions. Subscribe to security advisories for your top 10 dependencies. One outdated package can compromise everything else.

💬

5. Write Security-Conscious Prompts

Be explicit when using AI tools. Instead of "generate a login endpoint," say: "generate a secure login endpoint using parameterized queries, bcrypt hashing, JWT tokens, input validation, rate limiting, and logging for failed attempts." You get what you ask for.

The market gap: Traditional security agencies charge $10,000–$30,000 for a code audit. We built iFixVibeApps to deliver comprehensive security reviews in 48 hours for $500–$1,500 — specifically for founders and developers who built with AI tools and need real answers fast.