Перейти к содержимому

Dozor -- блоклисты из базы уязвимостей

NORA не держит базу уязвимостей. Это осознанное архитектурное решение (ADR-2): выгрузки OSV по экосистемам, которые NORA проксирует, весят около 360 МБ в сжатом виде и обновляются ежедневно, а сопоставление версий с ними требует индекса — то есть встроенной базы данных внутри процесса, который стартует за три секунды и в покое занимает меньше 50 МБ.

Dozor — это компаньон, который закрывает пробел снаружи. Он собирает фид OSV вместе с описью того, что реально лежит в вашем реестре, в blocklist.json того формата, который NORA уже умеет читать. Никаких изменений в NORA, никаких плагинов, никакой сети между ними — файл и есть весь интерфейс.

снапшот osv.dev ─┐
опись реестра ─┼─► dozor build ─► blocklist.json ─► git PR ─► NORA
policy.toml ─┘

Dozor — компилятор, а не сервис. У него нет демона, и он никогда не стоит на пути скачивания, поэтому не может уронить загрузку или задержать сборку.

Фид OSV по npm содержит 228 684 записи, и 96,8% из них — это MAL-*, сообщения о вредоносных пакетах, а не CVE: тайпсквоттинг, угнанные мейнтейнеры, отравленные релизы. У них вообще нет поля severity, поэтому политика, выраженная только порогом серьёзности, отбросила бы почти всё.

Сканер в CI видит вредоносный пакет только после того, как реестр уже закэшировал его и раздал команде. Реестр — единственное место, где отказ является предотвращением, а не обнаружением.

Скачайте бинарь со страницы релизов или соберите из исходников:

Окно терминала
git clone https://github.com/getnora-io/dozor
cd dozor && cargo build --release
Окно терминала
# 1. снимок фида (или принесите zip внутрь, если контур изолирован)
curl -o npm.zip https://osv-vulnerabilities.storage.googleapis.com/npm/all.zip
# 2. опись того, что лежит в NORA
dozor inventory --nora-data /var/lib/nora -o inventory.jsonl
# 3. сборка блоклиста
dozor build \
--feed-npm npm.zip \
--inventory inventory.jsonl \
--policy policy.toml \
--proactive \
-o blocklist.json
# 4. отдать NORA -- это вся конфигурация, которая нужна
export NORA_CURATION_MODE=enforce
export NORA_CURATION_BLOCKLIST_PATH=/etc/nora/blocklist.json

Флаг --proactive блокирует ещё и все целиком вредоносные пакеты из фида независимо от того, кэшировали вы их когда-нибудь или нет: пакет отказывается до первой загрузки.

version = 1
[defaults]
severity_threshold = "high" # critical | high | moderate | low | off
malicious = "block" # у MAL-* нет severity -- отдельный переключатель
[[exception]]
registry = "npm"
name = "lodash"
version = "4.17.20"
reason = "закреплено легаси-сборкой, отслеживается в JIRA-42"
expires = "2026-12-31" # обязательно -- просроченное исключение валит сборку

Строка reason, которую пишет Dozor, проходит через NORA нетронутой и попадает в тело ответа 403:

{
"error": "blocked_by_policy",
"context": {
"package": "lisa-bubursumsum21-miaww",
"version": "0.0.1-security.1",
"rule": "blocklist",
"reason": "MALICIOUS package (MAL-2025-115501) — blocked by dozor · https://osv.dev/vulnerability/MAL-2025-115501"
}
}

На выходе артефакт, а не мнение контроллера:

  • Детерминированность — одинаковые входы дают побайтово одинаковый выход, поэтому ежедневная задача открывает pull request только тогда, когда что-то действительно изменилось.
  • Проверяемость — каждый выход несёт блок x-dozor-derivation с дайджестами снапшота фида, описи, политики и самого тела. dozor verify выводит их заново, так что через месяцы можно доказать, почему сборка была заблокирована.
  • Ревьюируемость — блоклист лежит в git, и git log -S <пакет> отвечает на вопрос “когда мы начали это блокировать и кто одобрил”.

Ни оператора, ни CRD, ни петли согласования: задача, которая пишет файл.

dozor build вообще не ходит в сеть. Занесите zip с фидом тем же способом, которым переносите артефакты, а опись снимается с холодной копии каталога данных NORA. Больше ничего не нужно.

Dozor 0.1 покрывает npm. Все остальные экосистемы получают ответ Unknown, и Unknown никогда не превращается в “безопасно”: такие версии считаются и попадают в x-dozor-unmatched на выходе, а не проходят молча.

Следующий на очереди PyPI (PEP 440), затем семантика версий Maven, Go и RPM/deb.