# GuardDog Nexus Интеграция [GuardDog](https://github.com/DataDog/guarddog) (сканер уязвимостей пакетов) с [Sonatype Nexus Repository Manager]. Автоматически сканирует пакеты (PyPI, Go, npm), хранящиеся в Nexus, на наличие уязвимостей, вредоносного кода и подозрительных паттернов при каждом обновлении пакета через вебхуки. ## Возможности - **Автоматическое сканирование** по вебхукам Nexus при обновлении пакетов (только `UPDATED`) - **Поддержка нескольких экосистем** — PyPI, Go, npm (включая scoped-пакеты `@scope/name`); неизвестные экосистемы явно отклоняются - **REST API** для просмотра результатов сканирования, уязвимостей, статистики и экспорта в CSV - **Веб-интерфейс** с дашбордом, таблицами сканирований и фильтрацией по уязвимостям - **LLM-анализ** — автоматический разбор каждой уязвимости через OpenAI-совместимые API (опционально, настраивается); параллельный анализ через `asyncio.gather` - **Дедупликация** по URL и SHA256 — один и тот же пакет сканируется один раз - **Защита от SSRF** — валидация URL загрузки через `NEXUS_ALLOWED_HOSTS` - **Структурированное логирование** в формате 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 при необходимости (LLM и т.д.) # Запустите стек 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_ALLOWED_HOSTS` | хост из `NEXUS_URL` | Разрешённые хосты для скачивания (через запятую, защита от SSRF) | | `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://: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"}}' > ``` ### E2E-тестирование В `examples/` лежат примеры вредоносных пакетов для PyPI, npm и Go с паттернами, которые GuardDog гарантированно обнаружит (`exec-base64`, `shady-links`, `code-execution`, `npm-api-obfuscation`, `go-exec-base64`). Запуск E2E-теста (требует запущенного Docker-стека): ```bash ./examples/trigger-scans.sh ``` Скрипт собирает архивы `.tar.gz` / `.tgz` / `.zip`, копирует их в контейнер и отправляет вебхуки. Через ~15 секунд проверяет результаты: все три пакета должны быть flagged с findings. Для тестирования отдельной экосистемы — curl вручную: ```bash # PyPI: exec-base64 + shady-links + code-execution curl -X POST http://localhost:8080/webhooks/nexus \ -H "Content-Type: application/json" \ -d '{"action":"UPDATED","repositoryName":"pypi-proxy","asset":{"format":"pypi","name":"/packages/evil-pypi/0.1.0/evil-pypi-0.1.0.tar.gz","downloadUrl":"http://nexus:8081/repository/pypi-proxy/packages/evil-pypi/0.1.0/evil-pypi-0.1.0.tar.gz"}}' # npm: eval + Buffer(base64) + shady-links curl -X POST http://localhost:8080/webhooks/nexus \ -H "Content-Type: application/json" \ -d '{"action":"UPDATED","repositoryName":"npm-proxy","asset":{"format":"npm","name":"/packages/evil-npm/-/evil-npm-1.0.0.tgz","downloadUrl":"http://nexus:8081/repository/npm-proxy/evil-npm/-/evil-npm-1.0.0.tgz"}}' # Go: exec + base64 + suspicious HTTP call curl -X POST http://localhost:8080/webhooks/nexus \ -H "Content-Type: application/json" \ -d '{"action":"UPDATED","repositoryName":"go-proxy","asset":{"format":"go","name":"/packages/github.com/evil/evil-go/@v/v0.1.0.zip","downloadUrl":"http://nexus:8081/repository/go-proxy/github.com/evil/evil-go/@v/v0.1.0.zip"}}' ``` ## 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 | `/health/dependencies` | Проверка БД и доступности Nexus API | | 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) — убедитесь, что Nexus разрешает анонимное чтение репозиториев - Защита от SSRF: URL загрузки валидируется через `NEXUS_ALLOWED_HOSTS` - Заголовки безопасности на всех ответах: `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, `Permissions-Policy` - Результаты сканирования хранятся в локальной 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-окружении.