# Cómo corregir el SQL inseguro escrito por IA

> Los agentes de IA crean consultas SQL con interpolación de cadenas en lugar de declaraciones parametrizadas, introduciendo vulnerabilidades de inyección SQL en el código de base de datos en producción.

**Type:** Failure  
**Tools:** Cursor, Claude Code, Codex, Windsurf  
**Stack:** PostgreSQL, TypeScript  
**Updated:** 2026-06-08

---

El agente recurre a literales de plantilla para construir consultas SQL, creando vectores clásicos de inyección SQL que parecen funcionar bien en las pruebas pero son explotables en producción.

## El síntoma

Los valores controlados por el usuario se interpolan directamente en una cadena SQL.

```ts
// WRONG — SQL injection vulnerability
async function getUserByEmail(email: string) {
  const result = await db.query(
    `SELECT * FROM users WHERE email = '${email}'`
    //                                  ^^^^^^^ attacker-controlled
  );
  return result.rows[0];
}

// Attacker input: ' OR '1'='1
// Resulting query: SELECT * FROM users WHERE email = '' OR '1'='1'
// Returns every row in the table.
```

## Por qué ocurre

Los literales de plantilla son la herramienta más natural para construir cadenas en JavaScript, y muchos ejemplos de tutoriales en los que se entrenó el modelo los usan para SQL sin parametrización. El agente tampoco modela entradas adversarias: imagina datos bien formados que pasan a través.

## Cómo detectarlo

- Cadenas SQL que contienen interpolaciones `${...}`.
- Funciones de consulta que aceptan entrada del usuario y la pasan a un ayudante de consulta sin un arreglo de parámetros separado.
- `db.query(sql)` llamado con un solo argumento en lugar de `db.query(sql, [params])`.
- Patrones `LIKE '%${term}%'`.

## Cómo corregirlo

Siempre use consultas parametrizadas. El controlador de base de datos maneja el escape; su código nunca toca las comillas.

```ts
// CORRECT — parameterized query (node-postgres / pg)
async function getUserByEmail(email: string) {
  const result = await db.query(
    "SELECT id, name, email FROM users WHERE email = $1",
    [email]  // second argument: params array
  );
  return result.rows[0] ?? null;
}

// CORRECT — with Postgres.js (template tag)
async function searchUsers(term: string) {
  return sql`SELECT id, name FROM users WHERE name ILIKE ${"%" + term + "%"}`;
  // postgres.js automatically parameterizes template expressions
}
```

```txt
[ ] No ${...} inside raw SQL strings — use $1/$2 placeholders instead
[ ] Every db.query() call passes user input via the params array, not the SQL string
[ ] Use an ORM (Prisma, Drizzle) or query builder for complex queries
[ ] LIKE wildcards are appended in the param value, not concatenated into the SQL
[ ] Run sqlfluff or a SQL linter in CI to catch interpolated strings
```

## Indicación de corrección

```txt title="Fix Prompt"
This SQL query uses string interpolation with user-supplied values, which is a
SQL injection vulnerability. Rewrite every raw query to use parameterized
statements ($1, $2 placeholders for pg, or the tagged template literal form for
postgres.js). Never interpolate variables directly into SQL strings. If the
query is complex, migrate it to Prisma or Drizzle ORM instead.
```

## Prueba

```bash
# Detect template literal interpolation inside SQL-looking strings
grep -rn 'query(`\|sql`\|execute(`' --include="*.ts" --include="*.tsx" . \
  | grep '\${' \
  | grep -v "node_modules" \
  && echo "FAIL: interpolated SQL found" || echo "OK"
```