SC: SQL Injection
Purpose
Detects SQL injection vulnerabilities in all forms: classic (error-based), blind (boolean and time-based), UNION-based, second-order, and ORM bypass patterns. Traces user input from HTTP request parameters through application logic to database query construction, identifying points where unsanitized data enters SQL statements.
Activation
Called by sc-orchestrator during Phase 2 (Vulnerability Hunting). Runs against all detected languages.
Phase 1: Discovery
File Patterns to Search
**/*.go, **/*.ts, **/*.js, **/*.py, **/*.php, **/*.java, **/*.kt, **/*.cs,
**/*.rb, **/routes/*, **/controllers/*, **/models/*, **/repositories/*,
**/dal/*, **/dao/*, **/*query*, **/*sql*, **/*database*, **/*db*
Keyword Patterns to Search
# Direct SQL construction
"SELECT.*FROM" # SQL SELECT statements
"INSERT INTO" # SQL INSERT
"UPDATE.*SET" # SQL UPDATE
"DELETE FROM" # SQL DELETE
"EXEC ", "EXECUTE " # Stored procedure execution
# String concatenation in queries
"+ .*query" # String concat with query variable
`${.*}`.*SELECT # Template literals in SQL
f"SELECT, f"INSERT # Python f-strings in SQL
"WHERE.*=.*'" + " # String concat in WHERE clause
".format(.*)".*SELECT # str.format() in SQL
# Language-specific database calls
"db.Query(", "db.Exec(" # Go database/sql
"sequelize.query(", ".rawQuery(" # Node.js Sequelize
"cursor.execute(", "connection.execute(" # Python DB-API
"$wpdb->query(", "->whereRaw(" # PHP WordPress/Laravel
"createNativeQuery(", "createQuery(" # Java JPA
"FromSqlRaw(", "ExecuteSqlRaw(" # C# Entity Framework
Semantic Patterns
- String concatenation — any variable concatenated into a string that is later passed to a database query function
- Template literal interpolation — variables embedded in SQL via template literals or f-strings
- ORM raw query methods — framework ORM methods that accept raw SQL strings
- Stored procedure calls — dynamic stored procedure name or parameter construction
- Query builder misuse — using
.where() with raw strings instead of parameterized objects
Data Flow Tracing (Source → Sink)
Sources (user input):
req.query.*, req.params.*, req.body.* (Express/Node)
request.GET, request.POST, request.data (Django)
$_GET, $_POST, $_REQUEST, $_COOKIE (PHP)
r.URL.Query(), r.FormValue(), r.PathValue() (Go)
@RequestParam, @PathVariable, @RequestBody (Spring)
[FromQuery], [FromRoute], [FromBody] (ASP.NET)
Sinks (database queries):
db.Query(), db.Exec(), db.QueryRow() (Go)
sequelize.query(), knex.raw(), prisma.$queryRaw() (Node)
cursor.execute(), RawSQL(), .extra(), .raw() (Python)
$pdo->query(), mysqli_query(), DB::select(DB::raw()) (PHP)
createNativeQuery(), session.createSQLQuery() (Java)
.FromSqlRaw(), .ExecuteSqlRaw() (C#)
Phase 2: Verification
For each candidate finding:
Exploitability Checklist
- Can user-controlled input reach the SQL query without modification?
- Is the input inserted into the SQL structure (not just values)?
- Can the attacker control the position of quotes, operators, or keywords?
- Is there a way to observe the result (error messages, data differences, timing)?
Sanitization Check
- Is input passed through a parameterized query / prepared statement?
- Is input cast to a specific type (integer, UUID) before use?
- Is input validated against an allowlist of expected values?
- Is input escaped using a database-specific escape function?
Framework Protection Check
- Django ORM:
.filter(), .get(), .exclude() are safe. .raw(), .extra(), RawSQL() are not.
- SQLAlchemy:
session.query() with model attributes is safe. text() with f-strings is not.
- ActiveRecord:
.where(hash) is safe. .where("string #{var}") is not.
- Prisma: All standard methods are safe.
$queryRaw with template literal (tagged) is safe. $queryRawUnsafe is not.
- GORM:
.Where(struct) is safe. .Where("name = " + input) is not.
- Entity Framework: LINQ queries are safe.
.FromSqlRaw(interpolated) is not.
- Hibernate: Criteria API is safe. HQL with concatenation is not.
Context-Aware False Positive Elimination
- Is the "query" actually a search query against a search engine (Elasticsearch), not SQL?
- Is the variable a constant/enum, not user input?
- Is the concatenation building a query that uses parameterized placeholders?
- Is the code in a migration file, seed file, or schema definition (not runtime)?
Severity Classification
- Critical: Direct SQL injection from HTTP parameter into database query with no sanitization, affecting authentication or data retrieval. User can read/write/delete arbitrary data.
- High: SQL injection that requires specific conditions (e.g., certain parameter values, authenticated user) or affects limited data scope.
- Medium: Second-order SQL injection (stored input used later in query), or injection in admin-only endpoints.
- Low: SQL injection in dead code, test code, or with strong compensating controls (WAF, input validation that limits exploitation).
Language-Specific Notes
Go
// VULNERABLE: String concatenation in query
query := "SELECT * FROM users WHERE id = " + r.URL.Query().Get("id")
rows, err := db.Query(query)
// SAFE: Parameterized query
rows, err := db.Query("SELECT * FROM users WHERE id = $1", r.URL.Query().Get("id"))
TypeScript/JavaScript
// VULNERABLE: Template literal in raw query
const users = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE name = '${req.query.name}'`)
// SAFE: Tagged template (Prisma parameterizes these)
const users = await prisma.$queryRaw`SELECT * FROM users WHERE name = ${req.query.name}`
Python
# VULNERABLE: f-string in SQL
cursor.execute(f"SELECT * FROM users WHERE id = {request.GET['id']}")
# SAFE: Parameterized query
cursor.execute("SELECT * FROM users WHERE id = %s", [request.GET['id']])
PHP
// VULNERABLE: Direct interpolation
$result = $pdo->query("SELECT * FROM users WHERE id = " . $_GET['id']);
// SAFE: Prepared statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
Java
// VULNERABLE: String concatenation in JDBC
String query = "SELECT * FROM users WHERE id = " + request.getParameter("id");
ResultSet rs = stmt.executeQuery(query);
// SAFE: Prepared statement
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setString(1, request.getParameter("id"));
ResultSet rs = ps.executeQuery();
C#
// VULNERABLE: String interpolation in raw SQL
var users = context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Id = {id}").ToList();
// SAFE: Parameterized
var users = context.Users.FromSqlRaw("SELECT * FROM Users WHERE Id = {0}", id).ToList();
Output Format
Finding: SQLI-{NNN}
- Title: SQL Injection in {function/endpoint}
- Severity: Critical | High | Medium | Low
- Confidence: 0-100
- File: file/path:line
- Vulnerability Type: CWE-89 (SQL Injection)
- Description: User input from {source} is concatenated into SQL query at {sink} without parameterization.
- Proof of Concept: An attacker could supply
' OR 1=1 -- as the {parameter} to bypass the WHERE clause and retrieve all records.
- Impact: Unauthorized data access, data modification, authentication bypass, potential remote code execution via database features (xp_cmdshell, LOAD_FILE).
- Remediation: Use parameterized queries or prepared statements. Replace string concatenation with query placeholders.
- References: https://cwe.mitre.org/data/definitions/89.html, https://owasp.org/Top10/A03_2021-Injection/
Common False Positives
- ORM standard methods —
.filter(), .findOne(), LINQ queries are parameterized by default
- Prisma tagged templates —
prisma.$queryRaw\...`auto-parameterizes (but$queryRawUnsafe` does not)
- Search engine queries — Elasticsearch DSL, MongoDB queries are not SQL
- Schema/migration files — DDL statements in migration files are not user-input-driven
- Constants in queries —
"SELECT * FROM users WHERE role = 'admin'" with no variable input
- Query builders with safe API —
knex('users').where('id', input) parameterizes automatically
- Type-safe inputs — When input is guaranteed to be an integer via type casting before reaching query
1---2name: sc-sqli3description: SQL Injection detection across all variants — classic, blind, time-based, second-order, and UNION-based4license: MIT5---67# SC: SQL Injection89## Purpose1011Detects SQL injection vulnerabilities in all forms: classic (error-based), blind (boolean and time-based), UNION-based, second-order, and ORM bypass patterns. Traces user input from HTTP request parameters through application logic to database query construction, identifying points where unsanitized data enters SQL statements.1213## Activation1415Called by sc-orchestrator during Phase 2 (Vulnerability Hunting). Runs against all detected languages.1617## Phase 1: Discovery1819### File Patterns to Search20```21**/*.go, **/*.ts, **/*.js, **/*.py, **/*.php, **/*.java, **/*.kt, **/*.cs,22**/*.rb, **/routes/*, **/controllers/*, **/models/*, **/repositories/*,23**/dal/*, **/dao/*, **/*query*, **/*sql*, **/*database*, **/*db*24```2526### Keyword Patterns to Search27```28# Direct SQL construction29"SELECT.*FROM" # SQL SELECT statements30"INSERT INTO" # SQL INSERT31"UPDATE.*SET" # SQL UPDATE32"DELETE FROM" # SQL DELETE33"EXEC ", "EXECUTE " # Stored procedure execution3435# String concatenation in queries36"+ .*query" # String concat with query variable37`${.*}`.*SELECT # Template literals in SQL38f"SELECT, f"INSERT # Python f-strings in SQL39"WHERE.*=.*'" + " # String concat in WHERE clause40".format(.*)".*SELECT # str.format() in SQL4142# Language-specific database calls43"db.Query(", "db.Exec(" # Go database/sql44"sequelize.query(", ".rawQuery(" # Node.js Sequelize45"cursor.execute(", "connection.execute(" # Python DB-API46"$wpdb->query(", "->whereRaw(" # PHP WordPress/Laravel47"createNativeQuery(", "createQuery(" # Java JPA48"FromSqlRaw(", "ExecuteSqlRaw(" # C# Entity Framework49```5051### Semantic Patterns521. **String concatenation** — any variable concatenated into a string that is later passed to a database query function532. **Template literal interpolation** — variables embedded in SQL via template literals or f-strings543. **ORM raw query methods** — framework ORM methods that accept raw SQL strings554. **Stored procedure calls** — dynamic stored procedure name or parameter construction565. **Query builder misuse** — using `.where()` with raw strings instead of parameterized objects5758### Data Flow Tracing (Source → Sink)5960**Sources (user input):**61- `req.query.*`, `req.params.*`, `req.body.*` (Express/Node)62- `request.GET`, `request.POST`, `request.data` (Django)63- `$_GET`, `$_POST`, `$_REQUEST`, `$_COOKIE` (PHP)64- `r.URL.Query()`, `r.FormValue()`, `r.PathValue()` (Go)65- `@RequestParam`, `@PathVariable`, `@RequestBody` (Spring)66- `[FromQuery]`, `[FromRoute]`, `[FromBody]` (ASP.NET)6768**Sinks (database queries):**69- `db.Query()`, `db.Exec()`, `db.QueryRow()` (Go)70- `sequelize.query()`, `knex.raw()`, `prisma.$queryRaw()` (Node)71- `cursor.execute()`, `RawSQL()`, `.extra()`, `.raw()` (Python)72- `$pdo->query()`, `mysqli_query()`, `DB::select(DB::raw())` (PHP)73- `createNativeQuery()`, `session.createSQLQuery()` (Java)74- `.FromSqlRaw()`, `.ExecuteSqlRaw()` (C#)7576## Phase 2: Verification7778For each candidate finding:7980### Exploitability Checklist811. Can user-controlled input reach the SQL query without modification?822. Is the input inserted into the SQL structure (not just values)?833. Can the attacker control the position of quotes, operators, or keywords?844. Is there a way to observe the result (error messages, data differences, timing)?8586### Sanitization Check871. Is input passed through a parameterized query / prepared statement?882. Is input cast to a specific type (integer, UUID) before use?893. Is input validated against an allowlist of expected values?904. Is input escaped using a database-specific escape function?9192### Framework Protection Check93- **Django ORM:** `.filter()`, `.get()`, `.exclude()` are safe. `.raw()`, `.extra()`, `RawSQL()` are not.94- **SQLAlchemy:** `session.query()` with model attributes is safe. `text()` with f-strings is not.95- **ActiveRecord:** `.where(hash)` is safe. `.where("string #{var}")` is not.96- **Prisma:** All standard methods are safe. `$queryRaw` with template literal (tagged) is safe. `$queryRawUnsafe` is not.97- **GORM:** `.Where(struct)` is safe. `.Where("name = " + input)` is not.98- **Entity Framework:** LINQ queries are safe. `.FromSqlRaw(interpolated)` is not.99- **Hibernate:** Criteria API is safe. HQL with concatenation is not.100101### Context-Aware False Positive Elimination1021. Is the "query" actually a search query against a search engine (Elasticsearch), not SQL?1032. Is the variable a constant/enum, not user input?1043. Is the concatenation building a query that uses parameterized placeholders?1054. Is the code in a migration file, seed file, or schema definition (not runtime)?106107## Severity Classification108109- **Critical:** Direct SQL injection from HTTP parameter into database query with no sanitization, affecting authentication or data retrieval. User can read/write/delete arbitrary data.110- **High:** SQL injection that requires specific conditions (e.g., certain parameter values, authenticated user) or affects limited data scope.111- **Medium:** Second-order SQL injection (stored input used later in query), or injection in admin-only endpoints.112- **Low:** SQL injection in dead code, test code, or with strong compensating controls (WAF, input validation that limits exploitation).113114## Language-Specific Notes115116### Go117```go118// VULNERABLE: String concatenation in query119query := "SELECT * FROM users WHERE id = " + r.URL.Query().Get("id")120rows, err := db.Query(query)121122// SAFE: Parameterized query123rows, err := db.Query("SELECT * FROM users WHERE id = $1", r.URL.Query().Get("id"))124```125126### TypeScript/JavaScript127```typescript128// VULNERABLE: Template literal in raw query129const users = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE name = '${req.query.name}'`)130131// SAFE: Tagged template (Prisma parameterizes these)132const users = await prisma.$queryRaw`SELECT * FROM users WHERE name = ${req.query.name}`133```134135### Python136```python137# VULNERABLE: f-string in SQL138cursor.execute(f"SELECT * FROM users WHERE id = {request.GET['id']}")139140# SAFE: Parameterized query141cursor.execute("SELECT * FROM users WHERE id = %s", [request.GET['id']])142```143144### PHP145```php146// VULNERABLE: Direct interpolation147$result = $pdo->query("SELECT * FROM users WHERE id = " . $_GET['id']);148149// SAFE: Prepared statement150$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");151$stmt->execute([$_GET['id']]);152```153154### Java155```java156// VULNERABLE: String concatenation in JDBC157String query = "SELECT * FROM users WHERE id = " + request.getParameter("id");158ResultSet rs = stmt.executeQuery(query);159160// SAFE: Prepared statement161PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");162ps.setString(1, request.getParameter("id"));163ResultSet rs = ps.executeQuery();164```165166### C#167```csharp168// VULNERABLE: String interpolation in raw SQL169var users = context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Id = {id}").ToList();170171// SAFE: Parameterized172var users = context.Users.FromSqlRaw("SELECT * FROM Users WHERE Id = {0}", id).ToList();173```174175## Output Format176177### Finding: SQLI-{NNN}178- **Title:** SQL Injection in {function/endpoint}179- **Severity:** Critical | High | Medium | Low180- **Confidence:** 0-100181- **File:** file/path:line182- **Vulnerability Type:** CWE-89 (SQL Injection)183- **Description:** User input from {source} is concatenated into SQL query at {sink} without parameterization.184- **Proof of Concept:** An attacker could supply `' OR 1=1 --` as the {parameter} to bypass the WHERE clause and retrieve all records.185- **Impact:** Unauthorized data access, data modification, authentication bypass, potential remote code execution via database features (xp_cmdshell, LOAD_FILE).186- **Remediation:** Use parameterized queries or prepared statements. Replace string concatenation with query placeholders.187- **References:** https://cwe.mitre.org/data/definitions/89.html, https://owasp.org/Top10/A03_2021-Injection/188189## Common False Positives1901911. **ORM standard methods** — `.filter()`, `.findOne()`, LINQ queries are parameterized by default1922. **Prisma tagged templates** — `prisma.$queryRaw\`...\`` auto-parameterizes (but `$queryRawUnsafe` does not)1933. **Search engine queries** — Elasticsearch DSL, MongoDB queries are not SQL1944. **Schema/migration files** — DDL statements in migration files are not user-input-driven1955. **Constants in queries** — `"SELECT * FROM users WHERE role = 'admin'"` with no variable input1966. **Query builders with safe API** — `knex('users').where('id', input)` parameterizes automatically1977. **Type-safe inputs** — When input is guaranteed to be an integer via type casting before reaching query