check-sql-injection

Analyzes PHP code for SQL injection vulnerabilities. Detects query concatenation, ORM misuse, raw queries, dynamic identifiers, prepared statement bypasses.

59 stars

Best use case

check-sql-injection is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Analyzes PHP code for SQL injection vulnerabilities. Detects query concatenation, ORM misuse, raw queries, dynamic identifiers, prepared statement bypasses.

Teams using check-sql-injection should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/check-sql-injection/SKILL.md --create-dirs "https://raw.githubusercontent.com/dykyi-roman/awesome-claude-code/main/skills/check-sql-injection/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/check-sql-injection/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How check-sql-injection Compares

Feature / Agentcheck-sql-injectionStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Analyzes PHP code for SQL injection vulnerabilities. Detects query concatenation, ORM misuse, raw queries, dynamic identifiers, prepared statement bypasses.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# SQL Injection Security Check

Analyze PHP code for SQL injection vulnerabilities.

## Detection Patterns

### 1. String Concatenation in Queries

```php
// CRITICAL: Direct concatenation
$sql = "SELECT * FROM users WHERE id = " . $id;
$sql = "SELECT * FROM users WHERE email = '" . $email . "'";
$sql = "DELETE FROM posts WHERE id = $id";

// CRITICAL: In method
public function findByEmail(string $email): ?User
{
    $sql = "SELECT * FROM users WHERE email = '$email'";
    return $this->query($sql);
}
```

### 2. Variable Interpolation

```php
// CRITICAL: Double-quoted strings
$sql = "SELECT * FROM $table WHERE $column = '$value'";
$sql = "SELECT * FROM users WHERE name LIKE '%{$search}%'";

// CRITICAL: Heredoc/Nowdoc
$sql = <<<SQL
    SELECT * FROM users WHERE id = $id
SQL;
```

### 3. Dynamic Table/Column Names

```php
// CRITICAL: User-controlled identifiers
$table = $_GET['table'];
$query = "SELECT * FROM $table";

$column = $request->get('sort');
$query = "SELECT * FROM users ORDER BY $column";

// Even with prepared statements:
$stmt = $pdo->prepare("SELECT * FROM $table WHERE id = ?");
// Table name is still injectable!
```

### 4. ORM Raw Queries

```php
// CRITICAL: Doctrine raw DQL
$dql = "SELECT u FROM User u WHERE u.email = '$email'";
$query = $em->createQuery($dql);

// CRITICAL: Query builder with raw
$qb->where("u.status = $status");
$qb->orderBy($userInput);

// CRITICAL: Native query
$sql = "SELECT * FROM users WHERE name = '$name'";
$em->getConnection()->executeQuery($sql);
```

### 5. Eloquent/Laravel Raw

```php
// CRITICAL: Raw methods
User::whereRaw("email = '$email'")->get();
DB::select("SELECT * FROM users WHERE id = $id");
DB::raw("COUNT(*) as count WHERE status = '$status'");

// VULNERABLE: Order by
User::orderBy($request->input('sort'))->get();
User::orderByRaw($request->input('order'))->get();
```

### 6. LIKE Clause Injection

```php
// CRITICAL: Unescaped in LIKE
$sql = "SELECT * FROM products WHERE name LIKE '%$search%'";

// VULNERABLE: Wildcards not escaped
$stmt = $pdo->prepare("SELECT * FROM products WHERE name LIKE ?");
$stmt->execute(["%$search%"]);
// User can input: %' OR '1'='1

// CORRECT: Escape wildcards
$search = addcslashes($search, '%_');
$stmt->execute(["%{$search}%"]);
```

### 7. IN Clause Vulnerabilities

```php
// CRITICAL: Building IN unsafely
$ids = implode(',', $_GET['ids']);
$sql = "SELECT * FROM users WHERE id IN ($ids)";

// VULNERABLE: Array could contain non-integers
$ids = array_map('intval', $_GET['ids']);
$sql = "SELECT * FROM users WHERE id IN (" . implode(',', $ids) . ")";
// Empty array results in "IN ()" - SQL error

// CORRECT: Use parameter binding
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN ($placeholders)");
$stmt->execute($ids);
```

### 8. Second-Order Injection

```php
// CRITICAL: Data from database used unsafely
$user = $this->getUserFromDatabase($id);
$sql = "SELECT * FROM orders WHERE user_name = '" . $user->name . "'";
// If name was originally injected: O'Malley' OR '1'='1
```

### 9. Prepared Statement Bypasses

```php
// VULNERABLE: prepare() but not using parameters
$sql = "SELECT * FROM users WHERE id = $id";
$stmt = $pdo->prepare($sql);
$stmt->execute(); // No binding!

// VULNERABLE: Mixing bound and unbound
$stmt = $pdo->prepare("SELECT * FROM $table WHERE id = ?");
$stmt->execute([$id]); // Table still injectable
```

### 10. Stored Procedure Injection

```php
// CRITICAL: Procedure call with user input
$sql = "CALL process_order($orderId, '$status')";
$pdo->query($sql);

// CRITICAL: Even with prepare
$stmt = $pdo->prepare("CALL get_user_data('$userId')");
// Parameter inside string literal is not bound
```

## Grep Patterns

```bash
# Variable in SQL
Grep: '(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE).*\$\w+' -i --glob "**/*.php"

# Concatenation in query methods
Grep: "(query|execute|prepare)\s*\([^)]*\.\s*\\\$" --glob "**/*.php"

# Raw ORM methods
Grep: "(whereRaw|orderByRaw|selectRaw|DB::raw)\s*\(" --glob "**/*.php"

# DQL with variable
Grep: "createQuery\s*\([^)]*\\\$" --glob "**/*.php"

# LIKE without escaping
Grep: "LIKE.*'%.*\\\$.*%'" --glob "**/*.php"
```

## Severity Classification

| Pattern | Severity |
|---------|----------|
| Direct concatenation in SQL | 🔴 Critical |
| $_GET/$_POST in query | 🔴 Critical |
| Dynamic table/column name | 🔴 Critical |
| ORM raw with variable | 🔴 Critical |
| LIKE without wildcard escape | 🟠 Major |
| Second-order injection risk | 🟠 Major |

## Secure Patterns

### PDO Prepared Statements

```php
// Positional parameters
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ? AND status = ?');
$stmt->execute([$id, $status]);

// Named parameters
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
```

### Doctrine Query Builder

```php
$qb = $em->createQueryBuilder()
    ->select('u')
    ->from(User::class, 'u')
    ->where('u.email = :email')
    ->setParameter('email', $email);
```

### Safe Dynamic Identifiers

```php
// Whitelist for table/column names
$allowedTables = ['users', 'orders', 'products'];
if (!in_array($table, $allowedTables, true)) {
    throw new InvalidArgumentException('Invalid table');
}

$allowedColumns = ['name', 'email', 'created_at'];
$column = in_array($sort, $allowedColumns, true) ? $sort : 'id';
```

### Laravel Eloquent

```php
// Safe binding
User::where('email', $email)->first();

// With array
User::whereIn('id', $ids)->get();

// Order by validation
$allowed = ['name', 'created_at'];
$sort = in_array($input, $allowed) ? $input : 'id';
User::orderBy($sort)->get();
```

## Output Format

```markdown
### SQL Injection: [Description]

**Severity:** 🔴 Critical
**Location:** `file.php:line`
**CWE:** CWE-89 (SQL Injection)

**Issue:**
User input is included in SQL without parameterization.

**Attack Vector:**
Input: `' OR '1'='1' --`
Result: Query returns all rows, bypassing conditions.

**Code:**
```php
// Vulnerable code
```

**Fix:**
```php
// Parameterized query
```
```

## When This Is Acceptable

- **ORM query builders** — Doctrine/Eloquent query builders use parameter binding internally; they are safe by design
- **Named parameters in DQL/HQL** — `:paramName` syntax with `setParameter()` is not vulnerable
- **Schema migrations** — DDL statements in migrations don't process user input

### False Positive Indicators
- Code uses `$qb->where('field = :value')->setParameter('value', $input)`
- Code uses Doctrine DQL with bound parameters
- SQL is in a migration file with no user input

Related Skills

create-health-check

59
from dykyi-roman/awesome-claude-code

Generates Health Check pattern for PHP 8.4. Creates application-level health endpoints with component checkers (Database, Redis, RabbitMQ), status aggregation, and RFC-compliant JSON response. Includes unit tests.

create-docker-healthcheck

59
from dykyi-roman/awesome-claude-code

Generates Docker health check scripts for PHP services. Creates PHP-FPM, Nginx, and custom endpoint health checks.

check-xxe

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for XML External Entity vulnerabilities. Detects unsafe XML parsers, missing entity protection, LIBXML flags issues, XSLT attacks.

check-version-consistency

59
from dykyi-roman/awesome-claude-code

Audits version consistency across project files. Checks composer.json, README, CHANGELOG, docs, and configuration files for version number synchronization.

check-type-juggling

59
from dykyi-roman/awesome-claude-code

Detects PHP type juggling vulnerabilities. Identifies loose comparison with user input, in_array without strict mode, switch statement type coercion, and hash comparison bypasses.

check-timeout-strategy

59
from dykyi-roman/awesome-claude-code

Audits timeout configuration across HTTP clients, database connections, queue consumers, cache operations, and external service calls. Detects missing or misconfigured timeouts.

check-test-quality

59
from dykyi-roman/awesome-claude-code

Analyzes PHP test code quality. Checks test structure, assertion quality, test isolation, naming conventions, AAA pattern adherence.

check-ssrf

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for SSRF vulnerabilities. Detects unvalidated URLs, internal network access, DNS rebinding, cloud metadata access, URL parsing bypass attempts.

check-serialization

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for serialization overhead. Detects inefficient JSON encoding, large object hydration, missing JsonSerializable, circular reference issues.

check-sensitive-data

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for sensitive data exposure. Detects plaintext secrets, exposed credentials, PII in logs, insecure storage, hardcoded keys.

check-secure-headers

59
from dykyi-roman/awesome-claude-code

Audits HTTP security headers configuration. Checks CSP, X-Frame-Options, HSTS, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and cache control headers.

check-scalability-readiness

59
from dykyi-roman/awesome-claude-code

Analyzes PHP code for scalability issues. Detects file-based sessions, in-memory state, hardcoded hostnames, filesystem-dependent state, and missing stateless design patterns.