Metadata-Version: 2.5
Name: gqlhunter
Version: 0.3.0
Summary: GraphQL recon & analysis CLI — schema discovery, risk classification, IDOR candidates
Author: bess1lie
License: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: aiosqlite<1.0,>=0.20.0
Requires-Dist: httpx-sse<1.0,>=0.4.0
Requires-Dist: httpx<1.0,>=0.27.0
Requires-Dist: jinja2<4,>=3.1.0
Requires-Dist: markdown<4,>=3.6.0
Requires-Dist: pyyaml<7,>=6.0
Requires-Dist: rich<15,>=13.7.0
Requires-Dist: typer<1.0,>=0.12.0
Provides-Extra: dev
Requires-Dist: anyio>=4.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: respx>=0.21.0; extra == 'dev'
Requires-Dist: ruff>=0.3.0; extra == 'dev'
Description-Content-Type: text/markdown

<p align="center">
  <img src="social-preview.png" alt="gqlhunter" width="720">
</p>

<p align="center">
  <b>GraphQL recon & analysis CLI — find endpoints, dump schema, flag risky fields.</b><br>
  <sub>detection-only · scope-aware · bug-bounty ready</sub>
</p>

<p align="center">
  <a href="https://github.com/bess1lie/gqlhunter/actions"><img src="https://img.shields.io/github/actions/workflow/status/bess1lie/gqlhunter/ci.yml?branch=main&style=plastic" alt="CI"></a>
  <a href="https://pypi.org/project/gqlhunter/"><img src="https://img.shields.io/badge/PyPI-gqlhunter-3776AB?style=plastic&logo=pypi&logoColor=white" alt="PyPI"></a>
  <a href="https://github.com/bess1lie/gqlhunter/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-8b949e?style=plastic" alt="License"></a>
  <a href="https://github.com/bess1lie/gqlhunter/stargazers"><img src="https://img.shields.io/github/stars/bess1lie/gqlhunter?style=plastic" alt="Stars"></a>
</p>

<p align="center">
  <a href="https://bess1lie.github.io">🌍 bess1lie.github.io</a> ·
  <a href="https://github.com/bess1lie/apihunter">🔍 apihunter</a> ·
  <a href="https://github.com/bess1lie/bounthunt">🎯 bounthunt</a>
</p>

---

## 📦 Installation

```bash
# From PyPI (recommended) — detection-only, scope-aware
pip install gqlhunter
gqlhunter --help

# Isolated with pipx (recommended for tools)
pipx install gqlhunter

# From source (latest dev)
git clone https://github.com/bess1lie/gqlhunter.git
cd gqlhunter
pip install -e ".[dev]"
```

## ⚡ 30-second start

```bash
pip install gqlhunter   # or pipx install gqlhunter
# 1. scope — allowlist for your target (never scan out-of-scope)
cat > scope.yaml <<'YAML'
targets: ["https://example.com"]
allowlist: ["example.com"]
YAML

# 2. discover + scan + risk in one go
gqlhunter discover https://example.com --scope scope.yaml
gqlhunter scan https://example.com/graphql --scope scope.yaml

# 3. report + dashboard
gqlhunter report --db gqlhunter_*.db --format html -o report.html
gqlhunter dashboard --db gqlhunter_*.db
# -> http://127.0.0.1:8080
```

> **Why now:** 46k GraphQL issues found across 1500 APIs in 2024, 10% critical (Escape Tech). Introspection + batching + depth misconfigs are still the top 3.

---

## ✨ Features

| Feature | What it does | Output |
|---------|--------------|--------|
| **Discovery** | 18 common paths + POST fallback | live endpoints |
| **Introspection** | Configurable depth, handles `REQUIRES_AUTH/FILTERED` | `__schema` |
| **Risk** | `delete*/admin*/reset*` + IDOR args (`id, email, uuid`) | `critical/high/medium/low` |
| **Auth** | With vs without token, body-aware (`Unauthorized` in 200) | `public/auth_required/over_permissive` |
| **Diff** | Schema changes across 2 runs | added/modified/removed |
| **Variants** | alias, arg-removal, depth | query templates |
| **Export** | JSON clean + **SARIF 2.1.0** | `gqlhunter.sarif` for Code Scanning |
| **Notify** | Slack / Telegram / Webhook via Jinja | `notify` |
| **Dashboard** | SQLite + built-in HTTP UI | `/api/runs` |

---

## 🚀 Demo

<table>
<tr>
<td><b>discover</b> + <b>scan</b></td>
<td><b>report</b> + <b>dashboard</b></td>
</tr>
<tr>
<td>

```bash
$ gqlhunter discover https://example.com --scope scope.yaml
╭────────── Discovered ──────────╮
│ https://example.com/graphql 200 │
│ https://example.com/graphiql 200 │
╰──────────────────────────────╯

$ gqlhunter scan https://example.com/graphql --scope scope.yaml
Introspection: enabled (HTTP 200)
Stored 18 types, 8 queries, 3 mutations
Risk findings: 2 high, 1 medium
```

</td>
<td>
<img src="screenshots/dashboard-preview.png" alt="dashboard" width="360"><br>
<img src="screenshots/report-preview.png" alt="report" width="360">
</td>
</tr>
</table>

---

## 📖 Commands

| Command | Purpose | Example |
|---------|---------|---------|
| `discover` | Find GraphQL endpoints | `gqlhunter discover https://example.com --scope scope.yaml` |
| `scan` | Introspection + risk (single endpoint) | `gqlhunter scan https://example.com/graphql --scope scope.yaml --max-depth 5` |
| `batch` | Multi-target from `scope.yaml` | `gqlhunter batch scope.yaml --db batch.db` |
| `auth` | Token vs anon comparison | `gqlhunter auth https://example.com/graphql --scope scope.yaml --auth-header "Bearer x" --db scan.db` |
| `variants` | Query variant generation | `gqlhunter variants --db scan.db --strategy combinations` |
| `report` | HTML / Markdown | `gqlhunter report --db scan.db -o report.html` |
| `export` | JSON + SARIF | `gqlhunter export --db scan.db --output out/ --sarif` |
| `diff` | Last 2 runs diff | `gqlhunter diff --db batch.db` |
| `notify` | Slack/TG/webhook | `gqlhunter notify slack --db scan.db --webhook-url https://hooks.slack.com/...` |
| `dashboard` | Web UI | `gqlhunter dashboard --db scan.db` |

Full docs: [`docs/scope.md`](docs/scope.md) · [`docs/examples.md`](docs/examples.md) · [`docs/notifications.md`](docs/notifications.md)

---

## 🏗️ Architecture

```mermaid
flowchart LR
    T[Target] --> D[Discovery 18 paths + POST]
    D --> I[Introspection]
    I --> P[Parser]
    P --> A[Risk IDOR+BOLA]
    P --> Q[Query Builder]
    P --> U[Auth Analyzer]
    A --> DB[(SQLite)]
    Q --> DB
    U --> DB
    DB --> R[Report HTML/MD]
    DB --> E[Export SARIF]
    DB --> V[Dashboard]
    DB --> N[Diff/Notify]
```

**Stack:** Python 3.11+ · Typer · httpx (+sse) · SQLite (WAL) · Jinja2 · Docker

---

## 🛡️ Philosophy

| | |
|---|---|
| 🔍 | **Detection only** — never payloads, never exploitation |
| 🛡️ | **Scope-aware** — every request gated by `scope.yaml` |
| 📂 | **Open formats** — SQLite, HTML, SARIF 2.1.0 |
| 🧪 | **202 tests** in CI |

---

## 🗺️ Roadmap

| Feature | Status |
|---------|--------|
| Discovery (18 paths + POST) | ✅ |
| Introspection + risk + IDOR | ✅ |
| Auth body-aware (200+errors) | ✅ |
| Dashboard + diff + SARIF | ✅ |
| Batching amplification tester | 🚧 next |
| Depth/Complexity DoS tester | 🚧 |
| Clairvoyance (introspection off) | 🔮 |
| WebSocket subscriptions | 🔮 |

---

## ❓ FAQ

**Does it exploit?** No. It discovers and classifies. Variant queries include `# DO NOT AUTO-RUN` warning.

**Introspection disabled?** Marked `FILTERED/DISABLED`. Next: Clairvoyance-lite via `Did you mean ...?` reconstruction.

**Scope required?** Yes. `scope.yaml` with `targets`/`allowlist` is mandatory for `discover/scan/batch`.

**SARIF upload?** `gqlhunter.sarif` → GitHub Code Scanning. Version synced from `__version__`.

---

## 🤝 Contributing

```bash
git clone https://github.com/bess1lie/gqlhunter.git
cd gqlhunter
pip install -e ".[dev]"
pytest -q  # 202 tests
ruff check gqlhunter/ && ruff format --check gqlhunter/
pip install gqlhunter  # try published package smoke test
```

See [`CONTRIBUTING.md`](CONTRIBUTING.md) if present, else open an issue.

## 📄 License

MIT — see [`LICENSE`](LICENSE).

---

<p align="center">
  <sub>built in Almaty · <a href="https://github.com/bess1lie">bess1lie</a> · detection-first security tooling</sub>
</p>

[![Star History](https://api.star-history.com/svg?repos=bess1lie/gqlhunter&type=Date)](https://star-history.com/#bess1lie/gqlhunter&Date)
