Skip to main content
Glama
nizkl89

sqli-detector

by nizkl89
README.md
# SQL Injection Detector — MCP Blue-Team Tool

Lightweight SQLi scanner that runs as a **standalone CLI** or an **MCP server**
for AI-assisted security assessments.

## Features

- **3 detection strategies** — error-based, boolean-based, time-based
- **21 payloads** across 4 categories (error / boolean / time / union)
- **5 database engines** — MySQL, PostgreSQL, MSSQL, Oracle, SQLite
- **GET + POST form scanning** — extracts query params and HTML form fields
- **Cookie / header passthrough** — scan authenticated endpoints
- **Rate limiting** — configurable delay between requests to avoid WAF blocks
- **Baseline comparison** — reduces false positives on boolean detection
- **Dual interface** — CLI for interactive use, MCP for AI-host integration
- **40 unit tests** — pytest suite covering detection, crawling, injection, and payload integrity

## Requirements

- Python ≥ 3.10
- [uv](https://docs.astral.sh/uv) (package manager)

## Setup

```bash
uv sync
```

## CLI Usage

```bash
# Basic scan
uv run python main.py "http://target.com/page?param=1"

# Custom timeout (useful for time-based detection)
uv run python main.py "http://target.com/page?param=1" --timeout 15

# Authenticated scan with cookies
uv run python main.py "http://target.com/user?id=1" --cookie "session=abc123; token=xyz"

# Custom headers (repeatable)
uv run python main.py "http://target.com/api/user?id=1" \
  --header "Authorization: Bearer xyz" \
  --header "X-Forwarded-For: 10.0.0.1"

# Rate limiting — wait 1.5s between param tests
uv run python main.py "http://target.com/page?param=1" --delay 1.5

# Combined
uv run python main.py "http://target.com/user?id=1" \
  --cookie "session=abc123" --delay 0.5 --timeout 10
```

Exit code: **1** if vulnerabilities found, **0** if none detected.

## Testing Guide

### 1. Quick smoke test (local)

```bash
cd sql-injection-detector

# Terminal 1 — start test server
uv run python test_server.py

# Terminal 2 — scan it
uv run python main.py "http://127.0.0.1:5050/user?id=1"
uv run python main.py "http://127.0.0.1:5050/search?q=admin"
```

### 2. Test new features (P1 + P2)

```bash
# Custom headers
uv run python main.py "http://127.0.0.1:5050/user?id=1" \
  --cookie "session=abc123; token=xyz" \
  --header "X-Forwarded-For: 10.0.0.1" \
  --header "Authorization: Bearer test123"

# Rate limiting — 1 second between params
uv run python main.py "http://127.0.0.1:5050/user?id=1" --delay 1.0

# Combined
uv run python main.py "http://127.0.0.1:5050/user?id=1" \
  --cookie "session=test" --delay 0.5
```

### 3. Run the test suite

```bash
uv run pytest -v          # 40 tests
```

### 4. Test MCP server

```bash
(
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
sleep 0.3
echo '{"jsonrpc":"2.0","method":"notifications/initialized"}'
sleep 0.3
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"scan_url","arguments":{"url":"http://127.0.0.1:5050/user?id=1","timeout":5}}}'
) | uv run python mcp_server.py 2>/dev/null | python -m json.tool
```

### 5. Scan a real target

```bash
# Any public site you have permission to test
uv run python main.py "https://example.com/page?param=1" --timeout 15
```

## MCP Integration

The MCP server (`mcp_server.py`) exposes one tool — `scan_url` — that
returns structured JSON findings.

### OpenCode (`opencode.json`)

Add under `"mcp": {}`:

```json
"sqli-detector": {
  "type": "local",
  "command": [
    "uv", "run", "--directory",
    "/Users/kingsleylam/KinzDev/coding-test-ewalker/sql-injection-detector",
    "python", "mcp_server.py"
  ],
  "enabled": true
}
```

### Tool: `scan_url`

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `url` | `string` | — | Target URL (include query params) |
| `timeout` | `int` | `10` | HTTP request timeout in seconds |
| `headers` | `dict` | `null` | Optional HTTP headers (cookies, auth, etc.) |
| `delay` | `float` | `0.0` | Seconds between param tests (rate limiting) |

Returns JSON:

```json
{
  "target": "http://target.com/page?param=1",
  "vulnerable": true,
  "finding_count": 3,
  "findings": [
    {
      "param_name": "id",
      "payload": "'",
      "payload_name": "Single quote",
      "method": "error",
      "confidence": "High",
      "evidence": "DB error snippet...",
      "db_type": "SQLite"
    }
  ]
}
```

## Local Test Server

```bash
uv run python test_server.py
```

A intentionally-vulnerable Flask app on `http://127.0.0.1:5050` with endpoints:
- `GET /user?id=1` — SQLi via query param
- `POST /login` — SQLi via form body
- `GET /search?q=admin` — SQLi via search param

## Project Structure

```
├── main.py              CLI entry point
├── mcp_server.py        MCP stdio server
├── test_server.py       Local test harness (Flask)
├── pyproject.toml       Project metadata + deps (uv)
├── scanner/
│   ├── payloads.py      Payloads + DB error signatures
│   ├── crawler.py       URL param / form extraction
│   ├── injector.py      HTTP request sender
│   ├── detector.py      Detection engine (3 strategies)
│   └── reporter.py      CLI + JSON output formatters
└── tests/
    ├── conftest.py      Shared fixtures
    ├── test_detector.py Error, boolean, time, union detection
    ├── test_crawler.py  URL + HTML parsing
    ├── test_injector.py URL rewriting, baseline building
    └── test_payloads.py Payload structure + DB error regex
```

## Known Gaps

- **Serial scanning** — one param at a time, no concurrency (planned: asyncio)
- **Time-based susceptible to network jitter** — threshold tuned conservatively
- **Union detection is heuristic** — column-count probe only, no data exfiltration
- **No WAF bypass payloads** — no comment fragments, encoding variants, or case mutations
- **No automated OAST listener** — out-of-band detection not implemented

## License

Assessment project — not intended for production use.