Metadata-Version: 2.4
Name: ign8cve-tracker
Version: 0.1.0
Summary: Self-hosted CVE tracker — syncs NVD, scans assets via OpenVAS/OVAL/Insights, raises GitHub Issues on matches, ships a live status page.
License: MIT
Requires-Python: >=3.11
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Provides-Extra: dev
Provides-Extra: infra
Requires-Dist: aiohttp (>=3.9)
Requires-Dist: alembic (>=1.13)
Requires-Dist: apscheduler (>=3.10)
Requires-Dist: asyncpg (>=0.29)
Requires-Dist: cryptography (>=42.0) ; extra == "infra"
Requires-Dist: hcloud (>=2.0) ; extra == "infra"
Requires-Dist: httpx (>=0.27)
Requires-Dist: mypy (>=1.10) ; extra == "dev"
Requires-Dist: paramiko (>=3.4) ; extra == "infra"
Requires-Dist: pydantic (>=2.7)
Requires-Dist: pydantic-settings (>=2.3)
Requires-Dist: pygithub (>=2.3)
Requires-Dist: pytest (>=8) ; extra == "dev"
Requires-Dist: pytest-asyncio (>=0.23) ; extra == "dev"
Requires-Dist: pytest-httpx (>=0.30) ; extra == "dev"
Requires-Dist: python-gvm (>=24.6)
Requires-Dist: rich (>=13.0) ; extra == "infra"
Requires-Dist: ruff (>=0.4) ; extra == "dev"
Requires-Dist: sqlalchemy[asyncio] (>=2.0)
Requires-Dist: structlog (>=24.2)
Requires-Dist: tenacity (>=8.3)
Requires-Dist: typer (>=0.12) ; extra == "infra"
Description-Content-Type: text/markdown

# ign8cve-tracker

Self-hosted CVE tracker. Syncs the full NVD feed, discovers vulnerable software across your fleet via OpenVAS, OVAL scanning, and Red Hat Insights, raises a GitHub Issue for every new match, and ships a live status page.

```bash
pipx install "ign8cve-tracker[infra]"
ign8cve deploy   # provision Hetzner + deploy full stack
```

---

## What it does

| Component | What it does |
|---|---|
| `nvd-sync` | Full NVD sync on first run, incremental (`lastModified`) hourly after |
| `inventory-sync` | Pulls host + software records from OpenVAS via `python-gvm`, upserts assets |
| `oscap-sync` | Runs `oscap oval eval` on remote hosts over SSH, imports results |
| `insights-sync` | Fetches CVE exposure lists from Red Hat Insights for enrolled RHEL systems |
| `matcher` | Cross-joins assets × CVEs on CPE vendor+product, opens GitHub Issues for new matches |
| `status-page` | `aiohttp` dashboard: CVE counts by severity, match list, 30-day sparklines |
| `ign8cve` | CLI to provision/destroy the whole stack on Hetzner Cloud |

---

## Install

```bash
# just the daemons + status page
pip install ign8cve-tracker

# + deploy CLI (hcloud, paramiko, typer)
pip install "ign8cve-tracker[infra]"
```

---

## Deploy

Requires Hetzner Cloud and (optionally) Cloudflare credentials as environment variables:

```bash
export HETZNER_TOKEN=...
export IGN8_CLOUDFLARE_TOKEN=...   # optional — for DNS
export IGN8_CLOUDFLARE_ZONE_ID=... # optional
export IGN8_DOMAIN=example.com     # optional

ign8cve deploy
```

Provisions a Hetzner cx23, installs Docker, uploads the stack, obtains a Let's Encrypt cert, and runs all services. DNS is configured in Cloudflare before TLS so certbot can complete the HTTP-01 challenge.

State is written to `.ign8cve/state.json`. SSH key at `.ign8cve/keys/ign8cve`.

```bash
ign8cve destroy   # tear down server + DNS
```

---

## Run locally

```bash
cp .env.example .env   # fill in credentials

# individual daemons
nvd-sync
inventory-sync
matcher

# or all via Docker Compose
docker compose up
```

Database migrations:

```bash
alembic upgrade head
```

---

## Configuration

All settings via environment variables (or `.env`). Key variables:

| Variable | Default | Description |
|---|---|---|
| `DATABASE_URL` | `postgresql+asyncpg://ign8cve:ign8cve@localhost:5432/ign8cve` | PostgreSQL connection string |
| `NVD_API_KEY` | — | Raises NVD rate limit from 5 to 50 req/30 s |
| `NVD_SYNC_INTERVAL` | `3600` | Seconds between NVD syncs |
| `OPENVAS_HOST` | `localhost` | OpenVAS/GVM host |
| `OPENVAS_PORT` | `9390` | OpenVAS/GVM port |
| `OPENVAS_USERNAME` | `admin` | |
| `OPENVAS_PASSWORD` | — | |
| `GITHUB_TOKEN` | — | For opening Issues on new CVE matches |
| `GITHUB_REPO` | — | `org/repo` — e.g. `acme/vulnerabilities` |
| `INSIGHTS_OFFLINE_TOKEN` | — | Red Hat Insights offline token |
| `INSIGHTS_SYSTEMS` | — | JSON array: `[{"id": "<uuid>", "display_name": "<host>"}]` |

---

## Architecture

Three independent async daemons share a PostgreSQL database:

```
NVD API ──► nvd-sync ──► cves table
                                      ┐
OpenVAS/OVAL/Insights ──► inventory-sync / oscap-sync / insights-sync
                       ──► assets table ──► matcher ──► matches table ──► GitHub Issues
```

CPE matching is intentionally loose: vendor + product must match, version is ignored. This catches CVEs whose CPE wildcards the version. Precise version-range matching (`versionStartIncluding` / `versionEndExcluding`) is the main area for future improvement.

