Skip to content

Architecture

Andrii Sheiko edited this page Jul 17, 2026 · 3 revisions

Architecture

PyCVE побудований як static delivery pipeline: єдина «жива» частина системи — це builder, який періодично перетворює зовнішні CVE-фіди на статичні JSON-файли. Все інше — звичайна роздача файлів і локальний матчинг на хості.

Такий поділ дає кілька практичних властивостей:

  • Агенти не створюють навантаження на джерела даних. Скільки б хостів не перевірялося, Canonical і Debian бачать лише запити builder-а.
  • Роздача масштабується тривіально — це просто статика за CDN/nginx, без стану і без бекенду.
  • Хост не довіряє нічому, крім JSON-файлу. Агент не виконує код із мережі й не має write-доступу нікуди, крім власного кешу.

Потік даних

  1. rule-builder завантажує фід обраного джерела (Data Sources) і нормалізує його в єдиний формат правил (Rule Format).
  2. Для кожного релізу з --releases записується окремий файл: focal.json, jammy.json, bookworm.json, …
  3. Опційно збирається cve-priority.json — глобальний enrichment-файл (Priority).
  4. Статичний HTTP-сервер роздає каталог із цими файлами.
  5. local-agent на хості визначає свій дистрибутив і реліз, завантажує <base-url>/<release>.json, кешує його і матчить правила по локальному ядру та пакетах.

Артефакти

Каталог правил після збірки для змішаного парку виглядає так:

/srv/www/pycve/rules/
  focal.json          ─┐
  jammy.json           │ release rulesets:
  noble.json           │ визначають applicability —
  bookworm.json        │ «чи вразливий цей хост»
  bullseye.json        │
  trixie.json         ─┘
  cve-priority.json   ── enrichment: пріоритети, сортування,
                         анотації у виводі агента

Розподіл відповідальності між ними принциповий:

  • Release-файли відповідають за applicability. Тільки правило з release-файлу може зробити хост VULNERABLE.
  • cve-priority.json відповідає лише за пріоритезацію і відображення. Він ніколи не додає нових знахідок — лише сортує і анотує ті, що вже заматчились. Файл опційний: без нього агент працює, просто без пріоритетів.

У каталозі лежать тільки ті release-файли, які реально згенеровані обраним джерелом і списком --releases — «порожніх заглушок» для інших релізів немає.

Контракт каталогу правил

У тому самому каталозі можуть спокійно співіснувати release-рулсети, cve-priority.json і файл ручних пріоритетів. Це працює, бо в режимі --priority-only builder вважає рулсетом тільки JSON із top-level полем rules. Отже:

  • cve-priority.json не буде помилково прочитаний як release-рулсет;
  • manual-priority-file не буде прийнятий за рулсет;
  • у priority payload потрапляють лише CVE, які мають applicability-правила.

Стан довгого Canonical fetch

Повний обхід Canonical CVE feed — довга пагінована операція, тому builder веде checkpoint. Під час fetch поруч із каталогом правил можуть з'являтися:

canonical-feed.checkpoint.json               — легкий checkpoint із next_offset і pages_fetched
canonical-feed.checkpoint.json.records.jsonl — raw Canonical CVE records, по одному JSON на рядок

Після кожної успішно прочитаної сторінки checkpoint оновлюється, а витягнуті записи дописуються у records.jsonl. Перерваний запуск продовжується з next_offset, підхопивши вже накопичені записи. Після успішного завершення повного циклу обидва файли прибираються.

Важливе обмеження: частковий fetch не можна публікувати як продакшн-правила — рулсет, зібраний з половини фіду, виглядає валідним, але мовчки пропускає CVE. Якщо запуск обмежений --max-pages (наприклад, для тестів), направляйте його в окремий тестовий --output-dir.

Поведінка при збоях

Система спроєктована деградувати поступово, а не падати:

Збій Поведінка
cve-priority.json відсутній або недоступний Агент працює без enrichment: ті самі знахідки, без [priority: ...].
KEV або EPSS тимчасово недоступні Builder збирає priority payload без цього enrichment.
Canonical feed віддає timeout / 503 Builder робить retry з backoff і продовжує через checkpoint.
Агент не зміг завантажити рулсет Агент використовує локальний кеш, якщо той ще свіжий (у межах ttl).
Кеш протух і --allow-expired-cache не задано Агент повертає ERROR (exit code 1) — свідомо, щоб моніторинг побачив проблему, а не застарілий OK.
dpkg-query недоступний на хості Package- і kernel-package-перевірки не працюватимуть; коректні лише kernel-version-перевірки.

Розділення exit code 1 (ERROR) від 0 (OK) — навмисне: «не зміг перевірити» ніколи не маскується під «все добре».

Clone this wiki locally