-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
PyCVE побудований як static delivery pipeline: єдина «жива» частина системи — це builder, який періодично перетворює зовнішні CVE-фіди на статичні JSON-файли. Все інше — звичайна роздача файлів і локальний матчинг на хості.
Такий поділ дає кілька практичних властивостей:
- Агенти не створюють навантаження на джерела даних. Скільки б хостів не перевірялося, Canonical і Debian бачать лише запити builder-а.
- Роздача масштабується тривіально — це просто статика за CDN/nginx, без стану і без бекенду.
- Хост не довіряє нічому, крім JSON-файлу. Агент не виконує код із мережі й не має write-доступу нікуди, крім власного кешу.
-
rule-builderзавантажує фід обраного джерела (Data Sources) і нормалізує його в єдиний формат правил (Rule Format). - Для кожного релізу з
--releasesзаписується окремий файл:focal.json,jammy.json,bookworm.json, … - Опційно збирається
cve-priority.json— глобальний enrichment-файл (Priority). - Статичний HTTP-сервер роздає каталог із цими файлами.
-
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 CVE feed — довга пагінована операція, тому builder веде checkpoint. Під час fetch поруч із каталогом правил можуть з'являтися:
canonical-feed.checkpoint.json — легкий checkpoint із next_offset і status
canonical-feed.checkpoint.json.records.jsonl — raw Canonical CVE records, по одному JSON на рядок
Поле status у checkpoint має два значення:
-
in_progress— fetch був перерваний або обмежений; наступний запуск продовжить зnext_offset. -
complete— повний raw-mirror зібраний; наступні запуски можуть робити інкрементальне оновлення замість повного обходу.
Після успішного завершення повного циклу обидва файли прибираються.
Важливе обмеження: частковий 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) — навмисне: «не зміг перевірити» ніколи не маскується під «все добре».