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.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing