| name | sc-sqli |
| description | SQL Injection detection across all variants — classic, blind, time-based, second-order, and UNION-based |
| license | MIT |
| metadata | {"author":"ersinkoc","category":"security","version":"1.0.0"} |
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
query := "SELECT * FROM users WHERE id = " + r.URL.Query().Get("id")
rows, err := db.Query(query)
rows, err := db.Query("SELECT * FROM users WHERE id = $1", r.URL.Query().Get("id"))
TypeScript/JavaScript
const users = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE name = '${req.query.name}'`)
const users = await prisma.$queryRaw`SELECT * FROM users WHERE name = ${req.query.name}`
Python
cursor.execute(f"SELECT * FROM users WHERE id = {request.GET['id']}")
cursor.execute("SELECT * FROM users WHERE id = %s", [request.GET['id']])
PHP
$result = $pdo->query("SELECT * FROM users WHERE id = " . $_GET['id']);
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
Java
String query = "SELECT * FROM users WHERE id = " + request.getParameter("id");
ResultSet rs = stmt.executeQuery(query);
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setString(1, request.getParameter("id"));
ResultSet rs = ps.executeQuery();
C#
var users = context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Id = {id}").ToList();
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