Code Injection Prevention: SQL Injection, eval(), and Command Injection in AI Apps
AI tools generate code with eval(), raw SQL queries, and command execution. Here's how to find and fix injection vulnerabilities before attackers exploit them.
By Gabriel CA · Kraftwire Software
· 9 min readThe Injection Problem
Injection attacks happen when untrusted data is sent to an interpreter as part of a command or query. The attacker's data tricks the interpreter into executing unintended commands, reading data, modifying records, or even running system commands.
AI coding tools regularly generate injection-vulnerable code because they focus on making queries work, not on parameterizing them safely. The AI model does not think about what happens when someone deliberately sends malicious input. It generates the simplest working solution, which often means building queries with string concatenation.
This guide covers the four major types of injection attacks, explains exactly how they work, and shows you how to fix each one.
SQL Injection
SQL injection is the most well-known and most dangerous injection attack. It has been in the OWASP Top 10 for over two decades, and it is still one of the most common vulnerabilities in web applications.
How SQL Injection Works
AI-generated code often builds SQL queries with string concatenation:
// AI-generated code - SQL injection vulnerable
const query = `SELECT * FROM users WHERE email = '${userEmail}'`;
If `userEmail` is `' OR 1=1 --`, the query becomes:
SELECT * FROM users WHERE email = '' OR 1=1 --'
This returns every user in the database. The `--` comments out the rest of the query, so the trailing quote does not cause a syntax error.
What Attackers Can Do With SQL Injection
SQL injection is not just about reading data. Depending on the database permissions, an attacker can:
**Read entire tables** including user credentials, payment information, and private messages
**Modify data** by injecting UPDATE or INSERT statements
**Delete data** by injecting DROP TABLE or DELETE statements
**Bypass authentication** by injecting conditions that always evaluate to true
**Execute system commands** on some database configurations using features like `xp_cmdshell` (SQL Server) or `COPY TO PROGRAM` (PostgreSQL)
**Extract data incrementally** using blind SQL injection, where the attacker determines data one character at a time based on how the application responds
Real-World Impact
SQL injection has been responsible for some of the largest data breaches in history:
The 2017 Equifax breach exposed 147 million records through an unpatched SQL injection vulnerability
The Heartland Payment Systems breach in 2008 exposed 134 million credit card numbers
SQL injection remains the primary attack vector for web application data breaches
Fix: Use Parameterized Queries
// Safe - parameterized query with Supabase
const { data } = await supabase
.from("users")
.select("*")
.eq("email", userEmail);
If using raw SQL:
// Safe - parameterized query
const result = await pool.query(
"SELECT * FROM users WHERE email = $1",
[userEmail]
);
**Why parameterized queries work:** The database treats the parameter as data, not as part of the SQL command. Even if the parameter contains SQL syntax like `OR 1=1`, the database interprets it as a literal string value, not as a SQL operator.
**ORMs and query builders** like Prisma, Drizzle, and Supabase's client library use parameterized queries internally. If you use these tools correctly, you get SQL injection protection automatically. The risk comes when developers bypass the ORM and write raw SQL queries.
NoSQL Injection
NoSQL databases like MongoDB are also vulnerable to injection attacks, but the technique is different. Instead of injecting SQL syntax, attackers inject query operators.
How NoSQL Injection Works
// Vulnerable - user can pass { $gt: "" } as username
const user = await db.collection("users").findOne({
username: req.body.username,
password: req.body.password,
});
If an attacker sends `{ "$gt": "" }` as the username and password, the query becomes:
db.collection("users").findOne({
username: { $gt: "" }, // matches any non-empty username
password: { $gt: "" }, // matches any non-empty password
});
This returns the first user in the collection, effectively bypassing authentication entirely.
Why This Happens in AI-Generated Code
AI coding tools generate MongoDB queries that directly use request body data without type checking. The generated code assumes that `req.body.username` is a string, but Express.js (with `express.json()` middleware) parses JSON objects, so an attacker can send an object instead of a string.
Fix: Validate and Cast Input Types
// Safe - ensure inputs are strings
const username = String(req.body.username);
const password = String(req.body.password);
const user = await db.collection("users").findOne({
username,
password: await bcrypt.compare(password, storedHash),
});
**Additional protections:**
Use a validation library like Zod or Joi to validate request body shapes
Explicitly check that inputs are the expected type before using them in queries
Use MongoDB's `$eq` operator to prevent operator injection: `{ username: { $eq: inputValue } }`
eval() and Function() Constructor
`eval()` and `new Function()` take a string and execute it as JavaScript code. They are the most dangerous functions in the language because they give an attacker full code execution in your application context.
How AI Tools Generate eval() Vulnerabilities
AI coding tools sometimes use `eval()` when they need dynamic behavior:
// AI-generated - arbitrary code execution
const result = eval(userExpression);
// Also dangerous
const fn = new Function("return " + userInput);
const result = fn();
What Attackers Can Do
If `userExpression` is controlled by an attacker, they can:
**Read environment variables** containing API keys and database credentials
**Access the file system** to read configuration files or source code
**Make network requests** to exfiltrate data to external servers
**Modify application state** to bypass authentication or authorization
**Install persistent backdoors** that survive application restarts
Where eval() Appears in AI-Generated Code
We commonly find `eval()` in these contexts:
**Calculator features** where user input is evaluated as a math expression
**Template rendering** where user content is interpolated into strings
**Configuration parsing** where dynamic values are resolved at runtime
**Filter/sort logic** where user-defined criteria are executed as code
Fix: Use Safe Alternatives
// For math expressions, use a safe parser
import { evaluate } from "mathjs";
const result = evaluate(userExpression); // Only evaluates math, not arbitrary code
// For JSON parsing, use JSON.parse
const data = JSON.parse(userInput); // Only parses JSON, cannot execute code
// For dynamic property access, use bracket notation with validation
const allowedKeys = ["name", "email", "age"];
if (allowedKeys.includes(userKey)) {
const value = obj[userKey];
}
**The rule is simple:** Never pass user input to `eval()`, `new Function()`, `setTimeout(string)`, or `setInterval(string)`. There is always a safer alternative.
Command Injection
Command injection happens when user input is included in a shell command. AI-generated server code sometimes needs to run system commands for file processing, image conversion, or other operations.
How Command Injection Works
// AI-generated - command injection vulnerable
const output = execSync(`convert ${userFilename} output.png`);
If `userFilename` is `image.jpg; rm -rf /`, the server executes:
convert image.jpg; rm -rf / output.png
The semicolon terminates the first command and starts a new one. The attacker's command runs with the same permissions as your server process.
Other Command Injection Vectors
Beyond semicolons, attackers can use several characters to inject commands:
`&&` runs a second command if the first succeeds
`||` runs a second command if the first fails
`` ` `` (backticks) execute a command and substitute its output
`$(command)` does the same as backticks
`|` pipes the output of one command into another
`\n` (newline) starts a new command on some systems
Fix: Use execFile Instead of exec
// Safe - no shell interpretation
import { execFileSync } from "child_process";
const output = execFileSync("convert", [userFilename, "output.png"]);
**Why execFile is safe:** `execFile` runs the command directly without going through a shell. Each argument is passed separately to the program, so special characters like `;`, `&&`, and `|` are treated as literal parts of the filename, not as shell operators.
**Additional protections:**
Validate filenames against an allowlist of characters (alphanumeric, dots, hyphens, underscores)
Use a library like `shell-escape` if you absolutely must use `exec`
Run commands in a sandboxed environment with minimal permissions
Never run system commands with user input if you can avoid it
Injection Prevention Checklist
Never build SQL queries with string concatenation or template literals
Use parameterized queries, ORMs, or query builders for all database operations
Validate that NoSQL query inputs are the expected type (use `String()`, `Number()`, etc.)
Never use `eval()`, `new Function()`, `setTimeout(string)`, or `setInterval(string)` with user input
Use `execFile` instead of `exec` for system commands
Validate all input against expected patterns (allowlists, not blocklists)
Apply the principle of least privilege to database accounts
Use a validation library (Zod, Joi, Yup) on every API endpoint
Log and monitor for injection attempts (unusual characters in input fields)
Scan your codebase regularly for dangerous function calls
Scan for Injection Risks
SimplyScan's free scan checks 3 core categories. Go Pro for all 14 categories including injection, with 40+ checks that detect `eval()`, `Function()`, raw SQL concatenation, command injection patterns, and unsafe deserialization.
[Scan your app now](/)
Related Guides
[XSS Prevention Guide](/blog/xss-prevention-guide)
[CSRF Protection and Security Headers](/blog/csrf-security-headers-guide)
[How to Fix Exposed API Keys](/blog/fix-exposed-api-keys)
[MongoDB Security Guide](/blog/mongodb-security-guide)
[Is Vibe Coding Safe?](/blog/is-vibe-coding-safe)