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---6
7# SC: SQL Injection
8
9## Purpose
10
11Detects 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.
12
13## Activation
14
15Called by sc-orchestrator during Phase 2 (Vulnerability Hunting). Runs against all detected languages.
16
17## Phase 1: Discovery
18
19### File Patterns to Search
20```
21**/*.go, **/*.ts, **/*.js, **/*.py, **/*.php, **/*.java, **/*.kt, **/*.cs,
22**/*.rb, **/routes/*, **/controllers/*, **/models/*, **/repositories/*,
23**/dal/*, **/dao/*, **/*query*, **/*sql*, **/*database*, **/*db*
24```
25
26### Keyword Patterns to Search
27```
28# Direct SQL construction
29"SELECT.*FROM" # SQL SELECT statements
30"INSERT INTO" # SQL INSERT
31"UPDATE.*SET" # SQL UPDATE
32"DELETE FROM" # SQL DELETE
33"EXEC ", "EXECUTE " # Stored procedure execution
34
35# String concatenation in queries
36"+ .*query" # String concat with query variable
37`${.*}`.*SELECT # Template literals in SQL
38f"SELECT, f"INSERT # Python f-strings in SQL
39"WHERE.*=.*'" + " # String concat in WHERE clause
40".format(.*)".*SELECT # str.format() in SQL
41
42# Language-specific database calls
43"db.Query(", "db.Exec(" # Go database/sql
44"sequelize.query(", ".rawQuery(" # Node.js Sequelize
45"cursor.execute(", "connection.execute(" # Python DB-API
46"$wpdb->query(", "->whereRaw(" # PHP WordPress/Laravel
47"createNativeQuery(", "createQuery(" # Java JPA
48"FromSqlRaw(", "ExecuteSqlRaw(" # C# Entity Framework
49```
50
51### Semantic Patterns
521. **String concatenation** — any variable concatenated into a string that is later passed to a database query function
532. **Template literal interpolation** — variables embedded in SQL via template literals or f-strings
543. **ORM raw query methods** — framework ORM methods that accept raw SQL strings
554. **Stored procedure calls** — dynamic stored procedure name or parameter construction
565. **Query builder misuse** — using `.where()` with raw strings instead of parameterized objects
57
58### Data Flow Tracing (Source → Sink)
59
60**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)
67
68**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#)
75
76## Phase 2: Verification
77
78For each candidate finding:
79
80### Exploitability Checklist
811. 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)?
85
86### Sanitization Check
871. 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?
91
92### Framework Protection Check
93- **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.
100
101### Context-Aware False Positive Elimination
1021. 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)?
106
107## Severity Classification
108
109- **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).
113
114## Language-Specific Notes
115
116### Go
117```go
118// VULNERABLE: String concatenation in query
119query := "SELECT * FROM users WHERE id = " + r.URL.Query().Get("id")
120rows, err := db.Query(query)
121
122// SAFE: Parameterized query
123rows, err := db.Query("SELECT * FROM users WHERE id = $1", r.URL.Query().Get("id"))
124```
125
126### TypeScript/JavaScript
127```typescript
128// VULNERABLE: Template literal in raw query
129const users = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE name = '${req.query.name}'`)
130
131// SAFE: Tagged template (Prisma parameterizes these)
132const users = await prisma.$queryRaw`SELECT * FROM users WHERE name = ${req.query.name}`
133```
134
135### Python
136```python
137# VULNERABLE: f-string in SQL
138cursor.execute(f"SELECT * FROM users WHERE id = {request.GET['id']}")
139
140# SAFE: Parameterized query
141cursor.execute("SELECT * FROM users WHERE id = %s", [request.GET['id']])
142```
143
144### PHP
145```php
146// VULNERABLE: Direct interpolation
147$result = $pdo->query("SELECT * FROM users WHERE id = " . $_GET['id']);
148
149// SAFE: Prepared statement
150$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
151$stmt->execute([$_GET['id']]);
152```
153
154### Java
155```java
156// VULNERABLE: String concatenation in JDBC
157String query = "SELECT * FROM users WHERE id = " + request.getParameter("id");
158ResultSet rs = stmt.executeQuery(query);
159
160// SAFE: Prepared statement
161PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
162ps.setString(1, request.getParameter("id"));
163ResultSet rs = ps.executeQuery();
164```
165
166### C#
167```csharp
168// VULNERABLE: String interpolation in raw SQL
169var users = context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Id = {id}").ToList();
170
171// SAFE: Parameterized
172var users = context.Users.FromSqlRaw("SELECT * FROM Users WHERE Id = {0}", id).ToList();
173```
174
175## Output Format
176
177### Finding: SQLI-{NNN}
178- **Title:** SQL Injection in {function/endpoint}
179- **Severity:** Critical | High | Medium | Low
180- **Confidence:** 0-100
181- **File:** file/path:line
182- **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/
188
189## Common False Positives
190
1911. **ORM standard methods** — `.filter()`, `.findOne()`, LINQ queries are parameterized by default
1922. **Prisma tagged templates** — `prisma.$queryRaw\`...\`` auto-parameterizes (but `$queryRawUnsafe` does not)
1933. **Search engine queries** — Elasticsearch DSL, MongoDB queries are not SQL
1944. **Schema/migration files** — DDL statements in migration files are not user-input-driven
1955. **Constants in queries** — `"SELECT * FROM users WHERE role = 'admin'"` with no variable input
1966. **Query builders with safe API** — `knex('users').where('id', input)` parameterizes automatically
1977. **Type-safe inputs** — When input is guaranteed to be an integer via type casting before reaching query