Production-ready Textual TUI для Nexus Sonatype CE: просмотр репозиториев и ассетов, скачивание артефактов, сканирование через Grype, Trivy и/или OSV-Scanner и копирование только чистых (без уязвимостей) результатов в локальную verified-директорию.
python -m nexus_controlБыстрый старт: см. QUICKSTART.md.
- First-run wizard + XDG-конфиг (
~/.config/nexus-control/config.toml) — запуск из любого каталога - Клиент Nexus REST API (
httpx) с Basic Auth - Локальный кэш сессии (
~/.cache/nexus-control/session.json) с TTL — без лишних проверок авторизации - Список репозиториев с фильтром и обновлением
- Дерево ассетов по полю Nexus
path(раскрытие / сворачивание / фильтр) - Docker-репозитории через адаптер тегов (Registry v2 API + fallback по assets)
- Мультивыбор ассетов/папок (Space), затем download или verify; либо действие на весь репозиторий
- Фоновые workers для сети, скачивания, сканеров и копирования (UI не блокируется)
- Потоковое скачивание с защитой от path traversal
- Grype / Trivy / OSV-Scanner (локально или Docker-fallback); в TUI — клавиша
s - Строгая политика verify: в
<repo>-verifiedтолько если все включённые сканеры дали PASS - Ротация логов в файл + панель логов в TUI (секреты маскируются)
Слои разделены: UI/CLI вызывают сервисы; сервисы используют Nexus-клиент; безопасность путей — в utils/safe_path.py.
UI (Textual) / CLI / scheduler → services (pipeline, scanners, verifier)
→ nexus (REST client, cache, credentials)
→ utils (safe_path, fs, hashing, …)
- Python 3.13+ (или
uv, который подтянет нужный Python) - Целевая среда — Linux (TUI + POSIX-права). На Windows возможны unit-тесты и ограниченный UI
- Доступный Nexus Repository CE с REST API (
/service/rest/v1) - grype / trivy / osv-scanner в
PATHили Docker (fallbackanchore/grype/aquasec/trivy/ghcr.io/google/osv-scanner) - Опционально для docker-репозиториев: skopeo (предпочтительно) или docker CLI
# Нужен uv: https://docs.astral.sh/uv/
# ~/.local/bin должен быть в PATH
uv tool install git+https://github.com/meowsl/nexus-control.git@dev
nexus-control # Textual TUI
nexus-control-cli # headless verify/upload (cron / CI)При первом запуске wizard спросит Nexus URL и сохранит
~/.config/nexus-control/config.toml. Затем — prompt логина/пароля
(encrypted vault до TTL сессии).
После установки доступен nexus-control-cli — тот же pipeline, что в TUI, без UI:
# Список репозиториев
nexus-control-cli repos
# Verify + upload в <repo>-verified
nexus-control-cli verify --repo maven-hosted --upload
# Полный rescan без PASS-checkpoints (например, раз в неделю).
# FAIL снимается с remote *-verified вместе с checksum sidecar'ами:
nexus-control-cli verify --repo maven-hosted --upload --scan-mode full
# Только upload локального *-verified
# (PASS из verified-manifest.json; FAIL из failed_assets снимаются с remote)
nexus-control-cli upload --repo maven-hosted
# Smoke / узкий прогон
nexus-control-cli verify --repo maven-hosted --path-prefix com/example --limit 20 --json
nexus-control-cli verify --repo maven-hosted --exclude-prefix com/ --scan-limit 20
nexus-control-cli verify --repo maven-hosted --scan-limit 20 # debug: max mains to verify
# Параллельная загрузка/скан (по умолчанию auto от CPU/RAM; или явный override)
nexus-control-cli verify --repo maven-hosted --workers 8 --max-scanner-procs 4
# Порог severity (FAIL на high+; Low/Medium не блокируют verified)
nexus-control-cli verify --repo maven-hosted --severity high
# История сканирований (последние verify из TUI / CLI / scheduler)
nexus-control-cli history
nexus-control-cli history --repo maven-hosted --limit 20
nexus-control-cli history show <run_id>
nexus-control-cli history show <run_id> --json
# VK Teams / VK Workspace бот (уведомления scheduler)
nexus-control-cli vk-teams configure
nexus-control-cli vk-teams status
nexus-control-cli vk-teams test
nexus-control-cli vk-teams disable --clear-vault
# Offline OSV vulnerability DB (для osv / nuget verify без remote API)
nexus-control-cli osv-db status
nexus-control-cli osv-db update --ecosystem NuGet
# cron example:
# 0 3 * * * nexus-control-cli osv-db update --ecosystem NuGet
# Webhook: POST JSON-сводки после verify (Bearer / Basic / custom header)
nexus-control-cli webhook configure
nexus-control-cli webhook status
nexus-control-cli webhook test--limit N ограничивает только основные ассеты, которым действительно нужна
загрузка или перезагрузка из-за изменившегося checksum. Уже существующие
неизменённые файлы и checksum/signature sidecar'ы лимит не расходуют. Как только
найдено N таких main-ассетов, CLI останавливает pagination Nexus; частичный
список не сохраняется как полный asset cache. Companion sidecar'ы для PASS
запрашиваются напрямую по стандартным суффиксам, поэтому дочитывать весь
репозиторий ради них не требуется.
--scan-limit N — отдельный дебаг-лимит: в verify попадает не больше N
основных ассетов (с их sidecar'ами), независимо от того, нужна ли перезагрузка.
Удобно на больших репозиториях, когда --limit почти ничего не режет, потому
что локальный кэш уже заполнен. В scheduler: флаг schedule run … --scan-limit
или поле scan_limit в schedule.toml.
По умолчанию pipeline_workers = 0 и max_scanner_procs = 0 означают auto:
лимиты считаются от числа CPU и MemAvailable (~2 GiB на один concurrent
scanner, потолок 8). Явные значения в config / --workers /
--max-scanner-procs перекрывают auto. При старте verify печатается строка
Resource limits: ….
Глобальный семафор max_scanner_procs ограничивает одновременные процессы
сканеров across всех asset-workers (иначе workers × scanners легко
перегружает хост).
Диск: если volume с downloads/reports/verified заполнен выше
disk_critical_watermark (по умолчанию 95%), новые downloads не стартуют —
сканируется только уже локальное, затем пишутся отчёты. Архивации downloads
нет: tar.gz reclaim на том же диске не освобождал место (hardlink PASS) и
обрывал большие прогоны.
Verified без удвоения места: по умолчанию verified_link_mode=auto —
PASS кладётся в *-verified через hardlink на том же volume (download и
verified — один inode). Перекачка через .partial+replace не портит
уже linked verified. Если hardlink невозможен (другой FS) — обычный copy.
Уже существующие полные копии не конвертируются сами: один раз
OVERWRITE_VERIFIED=true или удалить *-verified и перепрогнать verify.
Жёсткий потолок снаружи процесса (рекомендуется для daemon):
# /etc/systemd/system/nexus-control-scheduler.service.d/override.conf
[Service]
MemoryMax=8G
CPUQuota=200%После полного PASS + verified copy рядом с локальным файлом сохраняется
*.scan-checkpoint.json. CLI verify / scheduler verify и verify_upload
в режиме scan_mode=incremental (по умолчанию) пропускают неизменённый PASS:
checksum, локальный файл, verified-копия, набор/версия/настройки сканеров и
severity совпадают. TTL возраста (scan_checkpoint_ttl) в incremental не
сжигает checkpoint — ежедневный verify_upload не пересканирует весь PASS.
FAIL/ERROR checkpoint не получают и сканируются каждый прогон. scan_mode=full
игнорирует checkpoints и пересканирует всё (отдельное правило, например на
субботу). Если ранее PASS-артефакт на полном rescan стал FAIL (новая CVE),
--upload / verify_upload удаляет его, checksum/signature sidecar'ы
(.md5/.sha1/…) и соседний Maven {artifactId}-{version}.pom (плюс checksum'ы POM)
из remote hosted *-verified и из локального *-verified. Classifier'ы
(-sources, -i18n) и maven-metadata.xml не трогаем.
ERROR сканера пакет не снимает. Учётной записи Nexus нужно право удалять
ассеты в hosted (не только upload). scan_checkpoint_ttl = 0 полностью
выключает skip.
Пропущенный PASS всё равно попадает в upload и verified-manifest.json, иначе
--upload не заливал бы уже проверенные артефакты.
Каждый verify (TUI / CLI / scheduler) также пишет компактный snapshot в
~/.cache/nexus-control/scan-history/ (index + runs/*.json). Хранится
последние scan_history_keep прогонов (по умолчанию 50; 0 = выкл).
В TUI: клавиша h — список; Enter — детали (как отчёт o). o без
in-memory отчёта открывает самый свежий disk-run для текущего репо.
Интерактивное меню для правил и локального демона (без systemd):
nexus-control-cli schedule # меню: list/add/edit/remove/start/stop/status/run/login
nexus-control-cli schedule login # сохранить зашифрованные креды для демона
nexus-control-cli schedule logout # очистить сохранённые scheduler-креды
nexus-control-cli schedule start
nexus-control-cli schedule stop
nexus-control-cli schedule status
nexus-control-cli schedule status -m # live progress (Ctrl+C)
nexus-control-cli schedule status -m --interval 0.5
nexus-control-cli schedule run nightly-core # фон; прогресс: status -m
nexus-control-cli schedule run nightly-core --foreground
nexus-control-cli schedule status -mПравила хранятся в ~/.config/nexus-control/schedule.toml (или $NEXUS_CONTROL_SCHEDULE).
Одно правило = одно cron-расписание + список репозиториев.
В меню Add/Edit показывается шпаргалка по полям cron и пресеты
(1 = каждый день 03:00, 2 = будни 03:00, …); можно ввести help или свой
5-field cron — перед сохранением CLI покажет ближайшие запуски.
[scheduler]
timezone = "local" # timezone машины; или IANA, напр. Europe/Moscow
overlap = "queue" # skip | queue | overlap (default: sequential catch-up queue)
[[rules]]
id = "nightly-core"
enabled = true
cron = "0 3 * * 1-5"
description = "Основные maven/npm"
repos = ["maven-hosted", "npm-hosted"]
action = "verify_upload"
# scan_mode = "incremental" # default: reuse unchanged PASS, still upload them
# scan_mode = "full" # ignore checkpoints; rescan everything
# targets = { "maven-hosted" = "maven-hosted-verified", "npm-hosted" = "npm-clean" }
[[rules]]
id = "weekend-raw"
enabled = true
cron = "30 4 * * 6"
repos = ["raw-hosted", "pypi-hosted"]
action = "verify"
upload = true
# path_prefixes = ["com/", "org/"]
# excluded_prefixes = ["com/"] # whole repo except com/; root xml staysДемон: pidfile в NEXUS_CACHE_DIR/scheduler.pid, лог — scheduler.log рядом с LOG_FILE.
Во время job демон пишет live-progress в scheduler-state.json; смотреть без логов:
schedule status -m / --monitor (обновление раз в --interval сек, по умолчанию 1).
Timezone по умолчанию — локальный TZ машины (timezone = "local": $TZ,
/etc/timezone, /etc/localtime). Явный IANA в schedule.toml перекрывает его.
SIGHUP перечитывает schedule.toml. После reboot демон нужно стартовать снова
(schedule start или внешний @reboot).
По умолчанию overlap = "queue": если слот правила уже наступил, а демон занят
другим job, правило встаёт в очередь и стартует сразу после текущего (последовательно).
Обработанные cron-слоты пишутся в scheduler-state.json (last_fires), поэтому
долгий скан в 02:00 не «съедает» задачу на 02:05.
Для daemon / schedule start|run нет интерактивного prompt. Задайте креды одним из способов:
NEXUS_USERNAME/NEXUS_PASSWORDв env или.env- Один раз:
nexus-control-cli schedule login— пароль вNEXUS_CACHE_DIR/credentials.scheduler.vault(Fernet,0o600), без TTL сессии; сброс:schedule logout
Session vault TUI (credentials.vault, TTL NEXUS_SESSION_TTL) для демона не считается долгоживущим источником.
После завершения правила планировщик может отправить итог в чат бота
VK Teams Bot API (VK Messenger для бизнеса / VK Workspace)
и, для action = "verify", показать кнопку Upload — по нажатию загрузит все
repos правила в *-verified. Это не почта и не VK ID.
- Создайте бота:
@Metabot→/newbot→ сохраните token. - Узнайте API URL инсталляции (часто
https://myteam.mail.ru/bot/v1; on-prem VK Workspace — свой URL у админов /@Metabot/start). - Узнайте chatId (ник, stamp группы или
…@chat.agent) и добавьте бота в чат. - Сохраните настройки (токен — в encrypted vault, не в
config.toml):
nexus-control-cli vk-teams configure
nexus-control-cli vk-teams status
nexus-control-cli vk-teams testВ config.toml остаются только не-секреты:
vk_teams_api_url = "https://myteam.mail.ru/bot/v1" # on-prem — свой URL
vk_teams_chat_id = "…"
vk_teams_notify = "always" # off | always | failures
vk_teams_upload_button = trueТокен: NEXUS_CACHE_DIR/vk-teams.vault (Fernet, тот же .vault_key, что у Nexus)
или, для CI, VK_TEAMS_TOKEN в окружении. Env/TOML перекрывают vault.
failures— только приexit_code != 0или FAIL/ERROR в истории прогона.- Сообщение по каждому репозиторию правила: заголовок («Плановое сканирование …» / «Сканирование …» при ручном
schedule run), интервал времени (Europe/Moscow), PASS/FAIL, кнопка Смотреть в DefectDojo (если были уязвимости и push в DD прошёл), кнопка Загрузить в Nexus дляaction=verify. - При
verify_uploadкнопка не показывается (upload уже выполнен). - Callbacks обрабатывает тот же scheduler daemon (long-poll
events/getв idle и коротким poll во время длинного verify). Upload идёт в фоне и не стопит cron. Без запущенного демона кнопка Upload не сработает. - Уведомления только от scheduler (
cron/schedule run), не из TUI и не изnexus-control-cli verify.
Смоук:
nexus-control-cli vk-teams test— в чате появляется сообщение о проверке связи.nexus-control-cli schedule start(или уже запущенный демон).- Дождитесь cron или
nexus-control-cli schedule run <verify-only-rule>. - В чате — сводка по репозиториям; для
action=verify— кнопка Загрузить. - Нажмите Загрузить → «Загружаю…» → сообщение обновится итогами; cron не зависает.
Выключить: nexus-control-cli vk-teams disable (опционально --clear-vault).
Альтернатива без встроенного демона — классический cron:
0 3 * * * NEXUS_USERNAME=… NEXUS_PASSWORD=… nexus-control-cli verify --repo maven-hosted --upload >>/var/log/nexus-verify.log 2>&1Docker-репозитории в CLI v1 не поддерживаются (используйте TUI).
git clone https://github.com/meowsl/nexus-control.git
cd nexus-control
uv sync --extra dev
uv run nexus-control
uv run nexus-control-cli reposИли классический venv:
python -m venv .venv && source .venv/bin/activate
pip install -e .
nexus-controlПриоритет (выше побеждает):
- Переменные окружения ОС (
NEXUS_URL, …) - Legacy
.envв текущем каталоге (опционально) ~/.config/nexus-control/config.toml(или$NEXUS_CONTROL_CONFIG)- Значения по умолчанию
Переопределить путь к TOML: export NEXUS_CONTROL_CONFIG=/path/to/config.toml.
Пример TOML — config.toml.example.
| Ключ / переменная | Описание |
|---|---|
nexus_url / NEXUS_URL |
Базовый URL Nexus, например http://localhost:8081 |
При отсутствии URL и наличии TTY запускается first-run wizard (язык → Nexus URL → TLS → сканеры → опционально DefectDojo).
| Переменная | Описание |
|---|---|
NEXUS_USERNAME / NEXUS_PASSWORD |
Опционально. Если не заданы — prompt при старте (TTY). Для CI задайте в env. |
После успешного логина в TUI пароль хранится зашифрованно (Fernet) в NEXUS_CACHE_DIR/credentials.vault только до expires_at Nexus-сессии (NEXUS_SESSION_TTL). В session.json пароля нет. Сброс: клавиша L (Logout) или истечение TTL.
Для планировщика отдельно: schedule login → credentials.scheduler.vault (без TTL сессии).
| Переменная | По умолчанию | Описание |
|---|---|---|
NEXUS_VERIFY_SSL |
true |
false только для self-signed TLS в лаборатории (будет warning) |
NEXUS_SESSION_TTL |
3600 |
TTL кэша сессии (секунды) |
NEXUS_CACHE_DIR |
~/.cache/nexus-control |
Каталог кэша сессии (режим 700) |
NEXUS_DOCKER_REGISTRY |
(пусто) | Переопределение docker connector host:port |
NEXUS_CONTROL_CONFIG |
~/.config/nexus-control/config.toml |
Путь к TOML-конфигу |
DOWNLOAD_ROOT |
~/nexus-control/downloads |
Скачанные артефакты |
REPORTS_ROOT |
~/nexus-control/reports |
JSON/TXT отчёты (grype_* / trivy_* / osv_*) |
VERIFIED_ROOT |
~/nexus-control |
Родитель каталогов <repo>-verified/ |
VERIFIED_LINK_MODE |
auto |
auto: hardlink download→verified на том же volume (без удвоения байт), иначе copy; copy: всегда полная копия |
SCANNERS |
grype |
Через запятую: grype, trivy, osv (в TUI — клавиша s) |
SEVERITY |
negligible |
Порог FAIL: critical | high | medium | low | negligible (TUI s, CLI --severity) |
DEFECTDOJO_ENABLED |
false |
После verify пушить FAIL findings в DefectDojo |
DEFECTDOJO_URL |
(пусто) | Базовый URL, например http://localhost:8080 |
DEFECTDOJO_API_KEY |
(пусто) | API token (или encrypted vault) |
GRYPE_USE_DOCKER |
auto |
auto / true / false |
GRYPE_DOCKER_IMAGE |
anchore/grype:latest |
Образ для docker-fallback |
TRIVY_USE_DOCKER |
auto |
auto / true / false |
TRIVY_DOCKER_IMAGE |
aquasec/trivy:latest |
Образ для docker-fallback |
OSV_USE_DOCKER |
auto |
auto / true / false |
OSV_DOCKER_IMAGE |
ghcr.io/google/osv-scanner:latest |
Образ для docker-fallback |
OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY |
~/.cache (XDG) |
Корень offline DB (osv-scalibr/<Eco>/all.zip) |
OVERWRITE_DOWNLOADS |
false |
Force-перекачка; иначе skip по checksum, mismatch → overwrite |
OVERWRITE_VERIFIED |
false |
Перезаписывать в verified |
SCAN_HISTORY_KEEP |
50 |
Сколько verify-прогонов хранить в истории; 0 = выкл |
VK_TEAMS_TOKEN |
(пусто) | Опционально: токен бота в env (CI). Предпочтительно vk-teams configure → vault |
VK_TEAMS_API_URL |
https://myteam.mail.ru/bot/v1 |
Base URL Bot API (on-prem — свой) |
VK_TEAMS_CHAT_ID |
(пусто) | chatId / nick / stamp для уведомлений |
VK_TEAMS_NOTIFY |
off |
off / always / failures (только scheduler) |
VK_TEAMS_UPLOAD_BUTTON |
true |
Кнопка Upload для action=verify |
LOG_FILE |
~/nexus-control/logs/nexus-control.log |
Ротируемый лог |
Все пути с ~ раскрываются через Path.expanduser(). Каталоги downloads / reports / verified / logs создаются при старте.
Legacy .env — см. .env.example. Для повседневного использования достаточно wizard / TOML.
python -m nexus_control
# или
nexus-control # TUI после install
nexus-control-cli # headless CLI
# или
python main.py- Запустите TUI против вашего Nexus.
- На экране репозиториев: фильтр
/, обновлениеr, открытиеEnter. - В дереве ассетов выберите файл или директорию.
s— выбрать сканеры и порог severity. Space — отметить файлы/папки;v— download → scan → copy PASS в verified (d— только download).- Нажмите
Vдля того же сценария по всему репозиторию. - Изучите модальное окно результатов; позже
o/h— последний отчёт и история.
| Клавиша | Действие |
|---|---|
q |
Выход |
r |
Обновить список |
/ |
Фильтр по имени |
Enter |
Открыть ассеты |
h |
История сканирований |
? |
Справка |
| Клавиша | Действие |
|---|---|
Esc / q |
Назад |
r |
Обновить ассеты |
/ |
Фильтр дерева |
Enter |
Раскрыть / свернуть |
d |
Скачать выбранное / отмеченное |
v |
Verify выбранного / отмеченного |
D |
Скачать весь репозиторий |
V |
Verify всего репозитория |
s |
Сканеры и порог severity |
o |
Последний отчёт (память или disk history) |
h |
История сканирований (текущий репо) |
? |
Справка |
Правила выбора
- Файл / образ → только этот элемент
- Директория → все вложенные ассеты
D/S/V→ всегда весь репозиторий
При значениях по умолчанию, пользователь alice, репозиторий my-repo:
| Тип | Путь |
|---|---|
| Downloads | /home/alice/nexus-control/downloads/my-repo/... |
| Reports | /home/alice/nexus-control/reports/my-repo/grype_....json / trivy_....json / osv_....json |
| Verified | /home/alice/nexus-control/my-repo-verified/... |
| Manifest | /home/alice/nexus-control/my-repo-verified/verified-manifest.json |
| Logs | /home/alice/nexus-control/logs/nexus-control.log |
| Session | ~/.cache/nexus-control/session.json |
Docker-образы сохраняются как:
downloads/<repo>/images/<tag-safe>.tar
- При старте читается
NEXUS_CACHE_DIR/session.json. - Кэш принимается только если совпадают
schema_version,nexus_url,username/config_hashиexpires_atещё в будущем. - Выполняется probe:
GET /service/rest/v1/repositories. - Если OK — сессия переиспользуется. При 401/403 кэш инвалидируется, выполняетсяется одна повторная авторизация; при повторном отказе — понятная ошибка пользователю.
- Пароль никогда не сохраняется. Права файла
600, каталога700(best-effort на POSIX). - Во время работы при 401/403: invalidate → re-auth один раз → retry.
Если Nexus поддерживает только Basic Auth (типичный CE), кэш всё равно хранит факт успешной проверки на TTL NEXUS_SESSION_TTL.
Включённые сканеры задаются SCANNERS (по умолчанию grype) или в TUI клавишей s. Порог SEVERITY / --severity / TUI s определяет, какие находки дают FAIL. При verify включённые сканеры могут работать параллельно. Отчёты: grype_<asset>.json|txt, trivy_<asset>.json|txt, osv_<asset>.json|txt (полный список findings; вердикт — по порогу).
OSV-Scanner всегда запускается с --experimental-plugins=directory,artifact (presets для directory + artifact extractors). Доп. флаги — через OSV_EXTRA_ARGS.
NuGet (.nupkg): сырой archive osv-scanner не разбирает. Для nuget-ассетов nexus-control делает identity-скан: читает .nuspec → временный custom lockfile → osv-scanner --lockfile osv-scanner:…. Grype/Trivy для таких ассетов помечаются SKIPPED (не влияют на aggregate). Нужен локальный osv-scanner (или Docker-fallback).
npm (.tgz / .tar.gz): сырой tarball Trivy/Grype не видят как пакет (trivy fs file.tgz → 0 language files). Перед сканом читается package/package.json из архива и пишется временный package-lock.json (identity) — Trivy/Grype сканируют его и находят CVE пакета.
OSV offline DB preflight: если нужен osv (--scanners osv или nuget-репо), перед verify проверяется локальная offline DB под ecosystem формата репо (pypi→PyPI, npm→npm, maven2→Maven, nuget→NuGet, rubygems→RubyGems, go→Go, apt→Debian, yum→Red Hat). Путь: ~/.cache/osv-scalibr/<Eco>/all.zip. Если DB нет — в TTY предложит скачать только нужный ecosystem; отказ / non-interactive → сканирование отменяется (без remote OSV API). raw/docker/helm/huggingface без package-ecosystem — preflight не блокирует. После успеха: --offline --offline-vulnerabilities. Обновление: nexus-control-cli osv-db update --ecosystem PyPI.
Порядок выбора бэкенда (для Grype / Trivy / osv-scanner CLI):
- Локальный бинарник, если найден и
*_USE_DOCKERне принудительноtrue - Иначе
docker runобраза (GRYPE_DOCKER_IMAGE/TRIVY_DOCKER_IMAGE/OSV_DOCKER_IMAGE), если режимauto/trueи docker доступен - Иначе понятная ошибка в UI / логах
Docker-сканеры монтируют DOWNLOAD_ROOT (ro), REPORTS_ROOT (rw) и кэш offline OSV DB (rw). Без privileged. Docker socket не монтируется.
Политика вердикта: FAIL, если у участвующего сканера есть находка не ниже порога severity (critical | high | medium | low | negligible). По умолчанию negligible — любая уязвимость, как раньше. Unknown всегда FAIL. SKIPPED в aggregate не учитывается. В verified копируется только итоговый PASS.
Порог задаётся в config.toml (severity = "high"), CLI --severity high, TUI (клавиша s) или поле severity у правила в schedule.toml. Смена порога инвалидирует PASS-checkpoint.
Опционально: после каждого verify (TUI / CLI / scheduler) findings с FAIL-ассетов уходят в DefectDojo как Generic Findings Import (POST /api/v2/reimport-scan/, auto_create_context).
- First-run wizard: «Включить DefectDojo?» → URL + API-ключ (ключ в
NEXUS_CACHE_DIR/defectdojo.vault, не в TOML). - Уже настроенный инстанс:
nexus-control-cli defectdojo configure/status/disable [--clear-vault]. - Env:
DEFECTDOJO_ENABLED,DEFECTDOJO_URL,DEFECTDOJO_API_KEY(и опционально product/engagement names). - Product по умолчанию
nexus-control, engagement = имя Nexus-репозитория. Ошибка push не роняет verify (warning в лог).
API-ключ: в UI DefectDojo → профиль → API Key.
Опционально: после каждого verify (TUI / CLI / scheduler) nexus-control шлёт POST JSON на ваш URL — сводка репозитория, totals, ассеты и уязвимости (без native dumps сканеров). Ошибка доставки не роняет verify (warning в лог).
- First-run wizard: «Включить вебхук?» → URL + auth.
- Уже настроенный инстанс:
nexus-control-cli webhook configure/status/test/disable [--clear-vault]. - Auth:
none|bearer(токен) |basic(логин/пароль) |header(свой заголовок, напримерX-Api-Key). Секреты — vault или env (WEBHOOK_TOKEN,WEBHOOK_USERNAME/WEBHOOK_PASSWORD,WEBHOOK_HEADER_VALUE), не TOML. - Заголовки запроса:
Content-Type: application/json,User-Agent: nexus-control/<version>,X-Nexus-Control-Event: verify.completed(дляwebhook test—webhook.test).
Тело verify.completed (сжато): event, source, version, repository,
времена, cancelled, scanners, totals, assets[] (path, kind,
verdict, scans с counts и до 20 CVE на сканер).
- Теги показываются под виртуальным узлом
images/(не сырые blob/manifest paths). - Источник тегов: Docker Registry v2
GET /v2/<repo>/tags/list, если известен host/port connector (NEXUS_DOCKER_REGISTRYилиattributes.docker.httpPort/httpsPort). - Fallback: вывод тегов из путей ассетов с
/manifests/<tag>. - Pull: skopeo
copyс временным auth-файлом (режим 600), иначе dockerpull+saveс временнымDOCKER_CONFIG— пароль не передаётся в argv.
- Nexus CE доступен по
NEXUS_URLи отдаёт REST v1. - Основная авторизация — HTTP Basic; cookies сохраняются, если сервер их выставляет.
- Docker connector в лабораториях часто на отдельном HTTP-порту; задайте
NEXUS_DOCKER_REGISTRY, если автоопределение не сработало. - Имя verified-каталога:
<repository>-verifiedподVERIFIED_ROOT(без хардкода/home/...). - При повторной загрузке локальный файл сверяется с remote checksum (
sha256→sha1→md5); совпадение → skip, расхождение → перекачка. После скачивания mismatch → downloadERROR(для blob-ассетов). Для npm package-root/metadata Nexus часто отдаёт sha1, не совпадающий с телом ответа — там checksum не hard-fail, skip идёт по неизменности remote identity в sidecar.OVERWRITE_DOWNLOADS=trueфорсирует перекачку. - Целевая ОС — Linux; биты прав на других платформах — best-effort.
- Upload verified создаёт hosted
<repo>-verifiedтого же format, что источник (npm/maven2/pypi/raw); npm metadata / non-package файлы при upload пропускаются; заливаются PASS из последнегоverified-manifest.jsonи их checksum/signature sidecar'ы (.md5/.sha1/…, без сканирования); stale-файлы в локальном*-verifiedне грузятся. FAIL из текущего verify (иfailed_assetsв манифесте) удаляются из remote*-verifiedвместе с sidecar'ами и Maven-модульным.pom; maven-metadata / archetype-catalog не трогаем - Нет произвольного admin write в Nexus (создание hosted
*-verified, upload PASS и delete только FAIL-ассетов в этом hosted) - Для docker нужны skopeo или docker CLI
- Очень большие репозитории загружают все ассеты в память для построения дерева (пагинация используется на проводе)
- Нет порога severity — по умолчанию строгий zero vulnerabilities
pytestОфлайн unit-тесты покрывают кэш сессии, построение дерева, парсер Grype и safe paths.
| Симптом | Что проверить |
|---|---|
| Ошибка конфигурации при старте | Есть .env; заданы обязательные NEXUS_* |
| Auth failed | Логин/пароль; права пользователя на репозитории |
| Ошибки TLS | Self-signed в лаборатории → NEXUS_VERIFY_SSL=false (ожидается warning) |
| Пустые docker-теги | Задайте NEXUS_DOCKER_REGISTRY=host:port |
| Grype/Trivy/OSV не найден | Установите бинарник или docker + образ; либо выключите сканер клавишей s |
| skopeo/docker pull падает | Auth к registry; для insecure HTTP помогает NEXUS_VERIFY_SSL=false |
| Путь отклонён | В path был .. или абсолютный путь — пропуск из соображений безопасности |
| UI «зависает» | Не должен; проверьте панель логов / файл лога |
- Пароль не пишется в кэш сессии, манифесты и отчёты
- Логи маскируют password / Authorization-подобные шаблоны
- Downloads ограничены
DOWNLOAD_ROOT; verified —VERIFIED_ROOT - Path traversal и абсолютные пути из API отклоняются
- Subprocess вызывается списками argv (
shell=False) - У скачанных файлов execute-биты снимаются best-effort
- Права кэша сессии ужесточаются на POSIX
- Токен VK Teams хранится в
vk-teams.vault(Fernet), не вconfig.toml - Временные auth-файлы docker/skopeo имеют режим
600и удаляются вместе с temp-каталогом
nexus-control/
├── main.py # тонкая обёртка: python main.py
├── pyproject.toml
├── requirements.txt
├── uv.lock
├── config.toml.example
├── schedule.toml.example # пример правил планировщика
├── .env.example # legacy env
├── QUICKSTART.md
├── README.md
├── scripts/ # вспомогательные скрипты
├── tests/ # pytest
└── nexus_control/ # пакет приложения
├── app.py / __main__.py # Textual TUI entry
├── config.py # pydantic-settings + XDG TOML
├── config_wizard.py # first-run setup
├── config_io.py / config_paths.py
├── models.py
├── logging_setup.py
├── i18n.py
├── cli/ # nexus-control-cli
│ ├── __main__.py # argparse: repos / verify / upload / schedule / history / vk-teams / …
│ ├── cmd_repos.py
│ ├── cmd_verify.py # download + scan + verified [+ upload]
│ ├── cmd_upload.py # upload локального *-verified без сканера
│ ├── cmd_schedule.py # интерактивное меню планировщика
│ ├── cmd_history.py # list/show scan history
│ ├── cmd_vk_teams.py # configure / status / test / disable
│ ├── cmd_osv_db.py # offline OSV DB status/update
│ ├── cmd_defectdojo.py # DefectDojo configure/status/disable
│ ├── cmd_webhook.py # webhook configure/status/disable/test
│ ├── assets.py # listing / cache / inspect / checkpoints
│ ├── progress.py
│ └── bootstrap.py
├── integrations/ # VK Teams, DefectDojo, webhook
│ ├── vk_teams.py # Bot API httpx-клиент + Fernet vault
│ ├── vk_notify.py # сообщения + Upload callback (фон)
│ ├── defectdojo.py
│ └── webhook.py
├── scheduler/ # schedule.toml + daemon (pidfile/cron loop)
│ ├── models.py / store.py / cronutil.py
│ ├── daemon.py / jobs.py / pidfile.py / state.py
│ └── paths.py
├── nexus/ # REST-клиент Nexus
│ ├── client.py
│ ├── repositories.py / assets.py / uploads.py
│ ├── session.py / credentials.py
│ └── asset_cache.py
├── services/
│ ├── pipeline.py # download → scan → verified copy
│ ├── downloader.py
│ ├── grype_scanner.py / trivy_scanner.py / osv_scanner.py
│ ├── scan_common.py / scan_checkpoint.py
│ ├── scan_history.py # index + run snapshots
│ ├── verifier.py / verified_uploader.py
│ └── docker_assets.py
├── ui/ # экраны / виджеты / history / кейбинды Textual
└── utils/ # safe_path, fs, hashing, subprocess, tree_builder
MIT — © 2026 meowsl