Dashboard

SQL Injection in AI-Generated Code: How to Find and Fix It

AI coding tools can write queries that glue user input into SQL. Four patterns to search for, with before and after code for each, and a prompt to audit.

Carlo Zuercher
Carlo Zuercher
Staff Engineer, Platform
3 October 20261 min read

SQL injection happens when code builds a database query by gluing user input into the SQL text, so an attacker can change what the query means. AI coding tools can produce that pattern, especially in quick prototypes, so it is worth searching a generated codebase for it. The fix is almost always the same: send the SQL and the user's values separately, using parameterized queries.

This guide is a search-and-fix routine you can run on your own project. The advice follows OWASP's SQL Injection Prevention Cheat Sheet, and the code examples are written for Node and PostgreSQL, though the idea carries to any stack.

What the problem is

OWASP describes SQL injection as arising when applications use dynamic database queries built with string concatenation and user-supplied input. An attacker supplies input that contains SQL, the database cannot tell the code from the data, and it runs the attacker's version.

Here is the shape of it:

javascript
// Vulnerable: the customer name becomes part of the SQL text
const sql = `select * from orders where customer = '${req.query.name}'`;
const result = await pool.query(sql);

If the name is an ordinary word, this works. If it contains a quote and extra SQL, the query changes. The code looks fine and passes every test with normal input, which is exactly why it survives reviews.

The fix OWASP recommends first

The primary defense is the prepared statement, also called a parameterized query. OWASP says to define all the SQL code first and pass in each parameter to the query later, so the database can always tell code from data. In node-postgres that looks like:

javascript
// Safe: SQL and values travel separately
const result = await pool.query(
  'select * from orders where customer = $1',
  [req.query.name]
);

The placeholder $1 is filled by the driver as a value, never as SQL. Nothing the user types can change the structure of the statement.

Four patterns to search for

Open your project and search for each of these. They cover most of how string-built SQL shows up in generated code.

1. Template literals and concatenation inside a query

Search for query calls that contain ${ or + joined to a variable:

javascript
// Vulnerable
pool.query("select * from users where email = '" + email + "'");
pool.query(`delete from carts where id = ${cartId}`);

// Fixed
pool.query('select * from users where email = $1', [email]);
pool.query('delete from carts where id = $1', [cartId]);

2. Raw query escape hatches in an ORM or query builder

Most ORMs are safe by default and unsafe when you drop to raw SQL. Search for raw, unsafe, queryRaw, or execute and read each call. Anything that interpolates user input into the raw string has the same problem as pattern 1. Use the tool's parameter or bindings feature for raw queries, and check its documentation for the exact syntax.

3. Dynamic table, column, or sort names

Placeholders cannot stand in for identifiers. A sort option from a URL is a classic case:

javascript
// Vulnerable: sort comes straight from the query string
pool.query(`select * from products order by ${req.query.sort}`);

// Fixed: allow-list the values you accept
const SORTABLE = { price: 'price', name: 'name', newest: 'created_at' };
const column = SORTABLE[req.query.sort] ?? 'created_at';
pool.query(`select * from products order by ${column}`);

OWASP calls this out specifically: for elements like table names, column names, or sort order indicators that cannot use bind variables, use allow-list input validation. The final query still interpolates column, but the value can only be one of your own strings.

4. Search boxes and LIKE filters

Search features are written fast and tested lightly. Look for like '%${term}%'. The fix keeps the percent signs in the value:

javascript
// Vulnerable
pool.query(`select * from posts where title like '%${term}%'`);

// Fixed
pool.query('select * from posts where title like $1', [`%${term}%`]);

What not to rely on

Escaping user input by hand is the tempting alternative. OWASP calls it fragile and says it cannot guarantee that this option will prevent all SQL injection in every situation, so keep it as a last resort rather than a plan. Input validation, such as checking that an id is a number, is a useful second layer, not a replacement for parameters.

Limiting what the database account can do also helps. OWASP lists least privilege as an additional defense: the account your app uses should not be able to drop tables or read data it never needs. If your backend talks to the database through an exposed API, the access rules are a separate layer, covered in how to check whether your app exposes user data.

A prompt to audit your codebase

Paste this into your AI coding assistant, then verify its findings by hand:

text
Search this project for every place that sends SQL to the database.
For each one, tell me:
1. The file and line.
2. Whether any part of the query text is built from a variable,
   request value, or template literal.
3. Whether that variable can contain user input.
Do not change anything yet. List the risky ones first, then propose
parameterized replacements for me to approve one at a time.

Asking for a report before edits keeps the change reviewable. The same habit applies to every generated change, as described in how to review AI-generated code before you ship it. Our overview of whether AI can find security vulnerabilities in your code explains where this kind of audit is strong and where it misses things, what Codex Security Cloud scans and what it will not shows what a hosted repository scanner covers, and signs your AI coding agent is about to ship a security bug lists warning signs to watch for during the build.

Test the fix

After switching to parameters, run the app with awkward input: a name containing an apostrophe, a very long string, an empty value. A parameterized query treats them all as plain text. If a normal user with an apostrophe in their name, such as O'Brien, breaks a screen, you probably still have string-built SQL somewhere.

FAQ

What is SQL injection in simple terms?

It is when user input is treated as part of the database command instead of as data, letting an attacker change what the query does.

Can AI-generated code have SQL injection vulnerabilities?

Yes. Any code that builds SQL from strings and user input can, regardless of who or what wrote it. Generated prototypes are worth checking because speed often wins over care.

Are ORMs immune to SQL injection?

They are safe by default when you use their normal query methods. Raw query features that interpolate user input bring the risk back.

What is the best way to prevent SQL injection?

OWASP's first recommendation is prepared statements with parameterized queries. Use allow-lists for identifiers such as sort columns, and give the database account only the permissions it needs.

How did this land?

About the author

Carlo Zuercher
Carlo Zuercher

Staff Engineer, Platform

Carlo works on the platform that turns prompts into running apps. He writes the engineering deep dives and the changelog notes worth reading.

Share

Get the next post in your inbox

One email a month. Product updates, engineering posts, and the best of Built with Swarmz.

I agree to receive emails about AI building tips and Swarmz product news. Unsubscribe any time.