# GuardDog Nexus

Интеграция [GuardDog](https://github.com/DataDog GuardDog) (сканер уязвимостей пакетов PyPI) с [Sonatype Nexus Repository Manager]. Автоматически сканирует Python-пакеты, хранящиеся в Nexus, на наличие уязвимостей, вредоносного кода и подозрительных паттернов при каждом обновлении или добавлении пакета.

## Возможности

- **Автоматическое сканирование** по вебхукам Nexus при создании/обновлении пакетов
- **Поддержка нескольких экосистем** — PyPI, Go, npm (любой формат через прокси-репозитории Nexus)
- **REST API** для просмотра результатов сканирования, уязвимостей, статистики и экспорта в CSV
- **Веб-интерфейс** с дашбордом, таблицами сканирований и фильтрацией по уязвимостям
- **LLM-анализ** — автоматический разбор каждой уязвимости через OpenAI-совместимые API (опционально, настраивается)
- **Дедупликация** по URL и SHA256 — один и тот же пакет сканируется один раз
- **Структурированное логирование** в формате JSON с опциональной отправкой в syslog
- **Docker Compose** для развёртывания приложения, Nexus и настройки в одном стеке

## Архитектура

```
Nexus ──(webhook)──> GuardDog Nexus ──(REST API)──> Веб-интерфейс
                         │
                         ├──> GuardDog CLI (сканирование)
                         ├──> LLM API (анализ уязвимостей)
                         ├──> SQLite (хранилище результатов)
                         └──> REST API (данные для UI + экспорт CSV)
```

## Быстрый старт

### Требования

- Docker и Docker Compose
- Python 3.10+ (для локальной разработки)

### Развёртывание в Docker

```bash
# Скопируйте файл конфигурации
cp .env.example .env

# Отредактируйте .env при необходимости
# NEXUS_PASSWORD=<ваш_пароль_администратора_Nexus>

# Запустите стек
make docker-up
```

После запуска доступны:

| Сервис | URL | Порт |
|--------|-----|------|
| GuardDog Nexus | http://localhost:8080 | 8080 |
| Sonatype Nexus | http://localhost:8081 | 8081 |

### Локальная разработка

```bash
# Установите зависимости
make install dev

# Настройте переменные окружения
cp .env.example .env
export $(cat .env | xargs)

# Запустите приложение
python -m guarddog_nexus.main
```

## Переменные окружения

| Переменная | По умолчанию | Описание |
|------------|-------------|----------|
| `NEXUS_URL` | `http://localhost:8081` | URL Sonatype Nexus |
| `NEXUS_USERNAME` | `admin` | Имя пользователя Nexus |
| `NEXUS_PASSWORD` | _(обязательно)_ | Пароль пользователя Nexus |
| `DATABASE_PATH` | `data/guarddog.db` | Путь к SQLite-базе данных |
| `HOST` | `0.0.0.0` | Хост для прослушивания |
| `PORT` | `8080` | Порт для прослушивания |
| `LOG_LEVEL` | `INFO` | Уровень логирования |
| `LOG_SYSLOG_HOST` | _(пусто)_ | Хост syslog для отправки логов |
| `LOG_SYSLOG_PORT` | `514` | Порт syslog |
| `WEBHOOK_SECRET` | _(пусто)_ | Секрет для HMAC-подписи вебхуков |
| `SCAN_TIMEOUT_SECONDS` | `300` | Таймаут сканирования одного пакета |
| `TEMP_DIR` | `/tmp/guarddog-nexus` | Временная директория для загрузки пакетов |
| `GUARDDOG_BINARY` | `guarddog` | Путь к бинарнику GuardDog |
| `NEXUS_DOWNLOAD_TIMEOUT_SECONDS` | `120` | Таймаут загрузки пакета из Nexus |
| `NEXUS_API_TIMEOUT_SECONDS` | `30` | Таймаут запросов к Nexus REST API |
| `MAX_CONCURRENT_SCANS` | `4` | Максимум одновременных сканирований GuardDog |
| `LOG_SYSLOG_FACILITY` | `local0` | Syslog facility (local0–local7) |
| `LLM_ENABLED` | `0` | `1` — включить LLM-анализ уязвимостей |
| `LLM_AUTO_ANALYZE` | `0` | `1` — автоанализ после скана; `0` = ручной режим через кнопку в UI |
| `LLM_API_KEY` | _(пусто)_ | API-ключ (OpenAI / Groq / Ollama / etc.) |
| `LLM_API_BASE` | `https://api.openai.com/v1` | Базовый URL OpenAI-совместимого API |
| `LLM_MODEL` | `gpt-4o-mini` | Название модели |
| `LLM_TIMEOUT_SECONDS` | `30` | Таймаут запроса к LLM |
| `LLM_MAX_CONCURRENT_ANALYSES` | `2` | Максимум одновременных LLM-анализов |

## Настройка Nexus

### Создание репозитория

1. Убедитесь, что в Nexus создан репозиторий `pypi-proxy` (прокси на `https://pypi.org`)
2. Настройте вебхук Nexus для отправки событий на `http://<guarddog-nexus>:8080/webhooks/nexus`
3. Используйте `scripts/setup-nexus.sh` для автоматической настройки (требует `curl`)

### Вебхуки

GuardDog Nexus принимает вебхуки от Nexus при событии `UPDATED` (срабатывает при обновлении кэша прокси-репозитория).

Для валидации вебхуков установите `WEBHOOK_SECRET` в `.env` — подпись проверяется через HMAC-SHA256.

> **Примечание:** Вебхуки доступны в Nexus Pro. В Nexus Repository Manager 3 Community Edition можно использовать плагин [nexus-blobstore-webhook](https://github.com/sonatype-nexus-community/nexus-blobstore-webhook) или настроить вебхуки вручную через административную панель.

### Настройка вебхуков в Nexus

1. В административной панели Nexus перейдите в **System → Capabilities**
2. Создайте capability типа **Webhook: Repository**
3. Укажите **URL:** `http://guarddog-nexus:8080/webhooks/nexus` (замените хост если нужно)
4. Выберите тип события: **Repository → Asset → Updated**
5. В фильтре репозиториев выберите: `pypi-proxy`, `go-proxy`, `npm-proxy`
6. Если задан `WEBHOOK_SECRET`, укажите тот же секрет в поле **Secret Key**

> Для локальной разработки без реальных вебхуков можно отправлять тестовые запросы вручную:
> ```bash
> curl -X POST http://localhost:8080/webhooks/nexus \
>   -H "Content-Type: application/json" \
>   -d '{"action":"UPDATED","repositoryName":"pypi-proxy","asset":{"format":"pypi","name":"/packages/pkg/1.0/pkg-1.0.tar.gz","downloadUrl":"http://nexus:8081/repository/pypi-proxy/packages/pkg/1.0/pkg-1.0.tar.gz"}}'
> ```

## REST API

### Сканирования

| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/api/v1/scans` | Список сканирований (пагинация, фильтр `flagged`) |
| GET | `/api/v1/scans/stats` | Статистика: общее количество, уязвимые пакеты, топ правил |
| GET | `/api/v1/scans/{id}` | Детали конкретного сканирования с результатами |
| GET | `/api/v1/scans/export` | Экспорт сканирований в CSV |

### Пакеты

| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/api/v1/packages` | Список уникальных пакетов (пагинация, фильтр по экосистеме) |
| GET | `/api/v1/packages/{name}/{version}` | Все сканирования и уязвимости для пакета |
| GET | `/api/v1/packages/export` | Экспорт пакетов в CSV |

### Уязвимости

| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/api/v1/findings` | Список уязвимостей (фильтр по правилу, severity, scan_id) |
| POST | `/api/v1/findings/{id}/analyze` | Запустить LLM-анализ уязвимости (возвращает HTMX-фрагмент при вызове из веб-интерфейса) |

### Здоровье и метрики

| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/health` | Проверка работоспособности |
| GET | `/metrics` | Метрики в формате Prometheus |

## Веб-интерфейс

| Страница | URL | Описание |
|----------|-----|----------|
| Дашборд | `/` | Статистика, графики, топ уязвимых пакетов |
| Сканирования | `/scans` | Таблица всех сканирований с фильтрацией |
| Детали сканирования | `/scans/{id}` | Результаты одного сканирования |
| Пакеты | `/packages` | Таблица уникальных пакетов |
| Детали пакета | `/packages/{name}/{version}` | История сканирований и уязвимости пакета |

## Структура проекта

```
guarddog-nexus/
├── guarddog_nexus/           # Основной пакет
│   ├── main.py               # Точка входа FastAPI
│   ├── config.py             # Конфигурация из переменных окружения
│   ├── constants.py          # Централизованные константы
│   ├── logging_setup.py      # JSON-логирование + syslog
│   ├── core/                 # Ядро: сканер, harvester, LLM
│   │   ├── scanner.py        # Интеграция с GuardDog CLI
│   │   ├── harvester.py      # Пайплайн: загрузка → скан → сохранение
│   │   ├── nexus.py          # HTTP-клиент + extractor'ы pypi/go/npm
│   │   └── llm.py            # LLM-клиент (OpenAI-совместимый)
│   ├── db/                   # База данных
│   │   ├── engine.py         # Async SQLAlchemy + миграции
│   │   ├── models.py         # ORM-модели (Scan, Finding)
│   │   └── queries.py        # Общие SQL-запросы
│   ├── routes/               # Роутеры FastAPI
│   │   ├── webhooks.py       # Приём вебхуков Nexus
│   │   ├── api_scans.py      # REST API: сканирования
│   │   ├── api_packages.py   # REST API: пакеты
│   │   ├── api_findings.py   # REST API: уязвимости
│   │   └── web.py            # Веб-интерфейс (Jinja2 + htmx)
│   └── web/                  # Статика и шаблоны
│       ├── templates/        # Jinja2-шаблоны
│       └── static/           # CSS, JS
├── tests/                    # Тесты pytest
├── scripts/                  # Вспомогательные скрипты
├── docker-compose.yml        # Стек Docker Compose
├── Dockerfile                # Образ приложения
└── pyproject.toml            # Зависимости и настройки
```

## Команды Makefile

| Команда | Описание |
|---------|----------|
| `make install` | Установка зависимостей проекта |
| `make dev` | Установка зависимостей для разработки |
| `make test` | Запуск тестов |
| `make lint` | Проверка кода через Ruff |
| `make format` | Форматирование кода через Ruff |
| `make docker-build` | Сборка Docker-образа |
| `make docker-up` | Пересборка и запуск стека (`up -d --build`) |
| `make docker-down` | Остановка стека |
| `make docker-destroy` | Остановка стека с удалением всех данных (`-v`) |
| `make docker-logs` | Просмотр логов стека |
| `make docker-rebuild` | Полная пересборка (down + build + up) |
| `make clean` | Очистка артефактов сборки |

## Безопасность

- Вебхуки поддерживают HMAC-SHA256 подпись через `WEBHOOK_SECRET`
- Nexus-клиент использует BasicAuth для аутентификации
- Результаты сканирования хранятся в локальной SQLite-базе
- Временные файлы пакетов удаляются после сканирования

## LLM-анализ

GuardDog Nexus может анализировать найденные уязвимости через LLM (языковую модель). При включении (`LLM_ENABLED=1`) уязвимые находки получают AI-разбор: насколько угроза реальна, что делает подозрительный код, рекомендации.

### Режимы работы

Переменная `LLM_AUTO_ANALYZE` управляет режимом анализа:

- **`LLM_AUTO_ANALYZE=1` (автоматический):** после завершения скана каждая находка автоматически отправляется в LLM. Отчёт сохраняется в БД и включается в syslog-событие. Кнопка анализа в UI не отображается.
- **`LLM_AUTO_ANALYZE=0` (ручной, по умолчанию):** в веб-интерфейсе рядом с каждой уязвимостью отображается кнопка «Analyze with LLM». Пользователь нажимает кнопку — запускается анализ, результат показывается inline.

### Состояния finding.report

Поле `finding.report` проходит через конечный автомат:

| Значение | UI |
|----------|----|
| `None` | Кнопка «Analyze with LLM» (только в ручном режиме) |
| `{"status": "analyzing"}` | Спиннер |
| `{verdict:, summary:, ...}` | Отчёт + ссылка «Retry» |

### Поддерживаемые провайдеры

Любой OpenAI-совместимый API. Примеры конфигурации:

```bash
# OpenAI (ручной режим)
LLM_ENABLED=1
LLM_AUTO_ANALYZE=0
LLM_API_KEY=sk-...
LLM_API_BASE=https://api.openai.com/v1
LLM_MODEL=gpt-4o-mini

# Groq с автоанализом (быстрее, бесплатный тир)
LLM_ENABLED=1
LLM_AUTO_ANALYZE=1
LLM_API_KEY=gsk_...
LLM_API_BASE=https://api.groq.com/openai/v1
LLM_MODEL=llama-3.3-70b-versatile

# Локальный Ollama
LLM_ENABLED=1
LLM_API_KEY=ollama
LLM_API_BASE=http://host.docker.internal:11434/v1
LLM_MODEL=llama3.2
```

### Формат ответа

LLM возвращает JSON с полями:
- `verdict` — `safe` / `suspicious` / `malicious`
- `summary` — вердикт в одно предложение
- `analysis` — подробный разбор (2–3 абзаца)
- `severity_rating` — `low` / `medium` / `high` / `critical`

Без LLM (`LLM_ENABLED=0`) весь остальной функционал работает как обычно.

## Лицензия

MIT

---

> **⚠ Весь код в этом репозитории сгенерирован AI-ассистентом (Claude).** Тщательно проверяйте код перед использованием в production-окружении.
