Files
guarddog-nexus/README.md

20 KiB
Raw Permalink Blame History

GuardDog Nexus

Интеграция 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

# Скопируйте файл конфигурации
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
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 (local0local7)
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 или настроить вебхуки вручную через административную панель.

Настройка вебхуков в 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

Для локальной разработки без реальных вебхуков можно отправлять тестовые запросы вручную:

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 /health/dependencies Проверка БД и доступности Nexus API
GET /metrics Prometheus-метрики: guarddog_scans_total, guarddog_scans_flagged_total, guarddog_findings_total, guarddog_llm_analyzed_total, guarddog_llm_pending_total, guarddog_scans_by_status, guarddog_scans_by_ecosystem, guarddog_last_scan_timestamp_seconds

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

Страница 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 Запуск тестов (168)
make lint Проверка кода через Ruff
make format Форматирование кода через Ruff
make typecheck Проверка типов через mypy (strict mode)
make check lint + format + typecheck + test (все проверки)
make run Запуск приложения локально
make setup-env Копирование .env.example.env (если отсутствует)
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 docker-ps Статус контейнеров (docker compose ps)
make docker-shell Интерактивная оболочка в контейнере
make docker-restart Перезапуск контейнера guarddog-nexus
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. Примеры конфигурации:

# 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 с полями:

  • verdictsafe / suspicious / malicious
  • summary — вердикт в одно предложение
  • analysis — подробный разбор (23 абзаца)
  • severity_ratinglow / medium / high / critical

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

Лицензия

MIT


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