18 KiB
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
# Скопируйте файл конфигурации
cp .env.example .env
# Отредактируйте .env при необходимости (LLM и т.д.)
# Запустите стек
make docker-up
После запуска доступны:
| Сервис | URL | Порт |
|---|---|---|
| GuardDog Nexus | http://localhost:8080 | 8080 |
| Sonatype Nexus | http://localhost:8081 | 8081 |
Локальная разработка
# Установите зависимости
make install dev
# Настройте переменные окружения
cp .env.example .env
export $(cat .env | xargs)
# Запустите приложение
python -m guarddog_nexus.main
Переменные окружения
| Переменная | По умолчанию | Описание |
|---|---|---|
NEXUS_URL |
http://localhost:8081 |
URL Sonatype 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
Создание репозитория
- Убедитесь, что в Nexus создан репозиторий
pypi-proxy(прокси наhttps://pypi.org) - Настройте вебхук Nexus для отправки событий на
http://<guarddog-nexus>:8080/webhooks/nexus - Используйте
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 или настроить вебхуки вручную через административную панель.
Настройка вебхуков в Nexus
- В административной панели Nexus перейдите в System → Capabilities
- Создайте capability типа Webhook: Repository
- Укажите URL:
http://guarddog-nexus:8080/webhooks/nexus(замените хост если нужно) - Выберите тип события: Repository → Asset → Updated
- В фильтре репозиториев выберите:
pypi-proxy,go-proxy,npm-proxy - Если задан
WEBHOOK_SECRET, укажите тот же секрет в поле Secret Key
Для локальной разработки без реальных вебхуков можно отправлять тестовые запросы вручную:
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-стека):
./examples/trigger-scans.sh
Скрипт собирает архивы .tar.gz / .tgz / .zip, копирует их в контейнер и отправляет вебхуки. Через ~15 секунд проверяет результаты: все три пакета должны быть flagged с findings.
Для тестирования отдельной экосистемы — curl вручную:
# 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 | /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. Примеры конфигурации:
# 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/malicioussummary— вердикт в одно предложениеanalysis— подробный разбор (2–3 абзаца)severity_rating—low/medium/high/critical
Без LLM (LLM_ENABLED=0) весь остальной функционал работает как обычно.
Лицензия
MIT
⚠ Весь код в этом репозитории сгенерирован AI-ассистентом (Claude). Тщательно проверяйте код перед использованием в production-окружении.