Skip to content

Agent Reference

Andrii Sheiko edited this page Nov 3, 2026 · 11 revisions

Agent Reference

Повний довідник CLI-агента local-agent (репозиторій pycve-agent): способи запуску, всі ключі, режими, формати виводу з усіма варіантами, поведінка кешу й сортування. Для першого знайомства почніть із Getting Started, для типових продакшн-сценаріїв — з Operations.

Способи запуску

Самодостатній бінарник — без Python на хості (CLI ідентичний local-agent, додатково відповідає на --version версією збірки):

./pycve-agent-linux-x86_64 --rules-base-url https://host/rules

Бінарники збираються Nuitka в Docker — див. packaging/nuitka/README.md у репозиторії агента. Стандартний варіант потребує glibc ≥ 2.34 (Ubuntu 22.04+, Debian 12+); для старих систем — зокрема focal і bullseye — є legacy-варіант із вимогою glibc ≥ 2.14. На Ubuntu/Debian обом варіантам, як і Python-обгортці, потрібен dpkg ≥ 1.16.2 (див. Operations).

Portable-обгортка — без venv і встановлення (потрібен лише Python 3.8+):

./run-local-agent.sh --rules-base-url https://host/rules
/opt/pycve-agent/run-local-agent.sh --rules-base-url https://host/rules

Прямий запуск із checkout-а:

PYTHONPATH=src python3 -m local_agent.cli --rules-base-url https://host/rules

Встановлений пакет (entrypoint local-agent):

local-agent --rules-base-url https://host/rules

Що відбувається при запуску

  1. Агент визначає platform і release з /etc/os-release, версію ядра — через uname; на Ubuntu додатково трек і повну версію збірки ядра, на Debian — версію збірки з uname -v (див. Rule Format). Для RHEL-сімейства використовується major VERSION_ID (10.2 → 10).
  2. Завантажує рулсет <rules-base-url>/<release>.json для Ubuntu/Debian або <rules-base-url>/<platform><release>.json для RPM-платформ і кешує його у --cache-path.
  3. Якщо доступний <rules-base-url>/cve-priority.json, кешує і його — в окремий файл із суфіксом .priority.
  4. Матчить правила по локальному ядру та пакетах (dpkg-query/Debian ordering або rpm/RPM EVR ordering). На Ubuntu/Debian правила прив'язані до source-пакетів, тому агент зіставляє їх зі встановленими binary-пакетами через ${source:Package} і порівнює source-версію (див. Rule Format). Пакети, які dpkg знає, але файлів яких на диску немає (стан rc після apt remove без purge, pn), не враховуються — за другою літерою ${db:Status-Abbrev}. Якщо встановлено кілька версій, а вразлива лише старіша, агент перевіряє, чи це залишок (див. Rule Format). Для цього потрібен root: без нього такий збіг лишається звичайною знахідкою.
  5. Шукає процеси, які після оновлення досі виконують видалений код (див. нижче).
  6. Друкує підсумок у stdout і завершується з відповідним exit code. Логи йдуть у stderr, тому 2>/dev/null лишає тільки результат.

Priority-файл використовується виключно для сортування та анотацій у виводі — applicability визначається лише рулсетом (див. Priority).

Ключі CLI

Ключ Дефолт Опис
--rules-base-url http://127.0.0.1:8080 Базовий URL каталогу зі static JSON-рулсетами.
--cache-path /tmp/local-agent-ruleset.json Файл кешу рулсету; priority кешується поруч із суфіксом .priority.
--timeout-seconds 5.0 Таймаут HTTP-запиту до static host.
--allow-expired-cache false Дозволити протермінований кеш, якщо свіжий fetch не вдався.
--os-release-path /etc/os-release Звідки визначати platform і release.
--platform авто Ручний override для platform.
--release авто Ручний override для release codename.
--kernel-version авто (uname) Ручний override версії ядра.
--kernel-only false Лишити тільки kernel-клас збігів (kernel_version, kernel_package_version).
--packages-only false Лишити тільки package-клас збігів (package_version).
--fixed-only false Лишити тільки precise-збіги з published fixed version.
--cve-limit 5 Максимум CVE-збігів у stdout; додатне ціле.
--show-all-cves false Вивести всі знайдені CVE; має пріоритет над --cve-limit.
--log-level INFO або $LOG_LEVEL Рівень логування.

Режими фільтрації

--kernel-only

Лишає тільки kernel_version і kernel_package_version matches:

  • kernel-проблема → VULNERABLE ... kernel ... (exit code залежить від priority, див. нижче);
  • якщо є лише package-проблеми → OK;
  • package-CVE в цьому режимі ігноруються;
  • процеси зі старим кодом не перевіряються.

--packages-only

Лишає тільки package_version matches:

  • package-проблема → VULNERABLE ... package ... (exit code залежить від priority, див. нижче);
  • якщо є лише kernel-проблеми і немає процесів зі старим кодом → OK;
  • kernel-CVE в цьому режимі ігноруються.

--fixed-only

За замовчуванням агент показує і precise version-based, і advisory-збіги. --fixed-only відсікає advisory:

  • CVE з match_mode = version залишаються;
  • CVE з match_mode = advisory прибираються з результатів;
  • комбінується з --kernel-only або --packages-only;
  • процеси зі старим кодом лишаються, бо оновлення вже встановлено.

Процеси зі старим кодом

Порівняння версій бачить пакет на диску, а не код у пам'яті. Якщо пакет оновлено, але служба не перезапущена, вона далі виконує стару вразливу версію. Агент вважає це відкритою вразливістю: такий хост не може бути OK, доки процес не перезапустять.

Як це визначається:

  • агент читає /proc/*/maps і шукає видалені файли з виконуваними відображеннями (x у правах);
  • враховуються лише системні шляхи: /lib*, /usr/lib*, /usr/libexec, /bin, /sbin, /usr/bin, /usr/sbin. Так відсіюються memfd, /dev/zero, сегменти /SYSV…, дані на кшталт locale-archive і програми поза системними каталогами;
  • перевірка працює лише для локального хоста. З --platform, --release або --kernel-version вона не виконується, бо такі запуски описують інший хост.

Обмеження:

  • Агент не може назвати CVE такого процесу, бо стара версія пакета вже невідома. Тому код виходу тут базовий, 2.
  • Без root процеси інших користувачів не видно. Агент друкує попередження WARNING: restart check incomplete, run as root, а код виходу через це не змінюється.
  • Назва процесу береться з /proc/<pid>/comm, тому обрізається до 15 символів (systemd-timesyn).

Exit codes

Exit code відображає найвищий priority серед знайдених збігів, а не тип перевірки (kernel/package — це видно тільки з тексту повідомлення). Priority береться з cve-priority.json (поле priority у записі CVE); is_kev і ручний override (is_manual) на сервері вже підіймають priority до critical, тож окремо їх на боці агента не треба враховувати.

Код Статус Значення
0 OK жодне правило не заматчилось і немає процесів зі старим кодом
1 ERROR перевірку виконати не вдалося
2 VULNERABLE є збіг з priority low/medium/unknown (або priority payload недоступний), або є процеси зі старим кодом
3 VULNERABLE_HIGH є збіг з priority high
4 VULNERABLE_CRITICAL є збіг з priority critical (включно з KEV і ручним override)

Якщо збігів декілька — береться найвищий priority серед них (той самий збіг, що йде першим у виводі після сортування).

Збіги з [leftover] у цьому виборі не беруть участі. Якщо знайдено лише такі збіги, exit code дорівнює 2 незалежно від їхнього priority.

Процеси зі старим кодом дають щонайменше 2. Якщо CVE дають вищий код, лишається вищий.

Формати виводу

Формат однаковий для всіх платформ: одна CVE — один рядок. Нижче — всі варіанти stdout, які може дати поточна версія агента. Це корисно, якщо ви парсите вивід зовнішнім тулінгом.

OK і ERROR

OK: no matching CVE rules found
ERROR: <message>

Процеси зі старим кодом

Цей блок завжди друкується першим, а --cve-limit його не обрізає:

VULNERABLE: <count> processes still run old code after a package update, restart required
- <process> (pid <pid>): <file>, <file>

Для одного процесу перший рядок має вигляд VULNERABLE: 1 process still runs old code after a package update, restart required. Якщо є й CVE, далі йде їхній звичайний вивід зі своїм рядком VULNERABLE:.

Без root наприкінці виводу додається рядок:

WARNING: restart check incomplete, run as root

Рядок CVE

<CVE> (<title>) on <platform> <release>: <частина>; <частина> [priority: <priority>, known exploited]
  • (<title>) є лише в CVE з ручним title.
  • Частин стільки, скільки різних пакетів чи ядер зачіпає CVE. Частини ядра йдуть першими, а частини з [leftover] останніми.
  • [priority: ...] один на рядок. Його немає, якщо priority-файл недоступний або в ньому немає цієї CVE. known exploited додається, якщо is_kev = true.

Частини рядка

Точні збіги:

package <package_name> <package_version>
package <package_name> (binaries: <binary>, <binary>) <package_version>
package <package_name>, <package_name> <package_version>
package <package_name> <package_version> [leftover]
kernel package <kernel_package_version> for kernel <kernel_version>
kernel <kernel_version>
kernel <kernel_version> (build <build_version>)

Збіги без опублікованого виправлення (advisory):

advisory package <package_name> <package_version> (<status>, no fixed version)
advisory package <package_name> (binaries: <binary>, <binary>) <package_version> (<status>, no fixed version)
advisory kernel package <kernel_package_version> (<package_name> <status>, no fixed version)
advisory kernel <kernel_version> (track <package_name> <status>, no fixed version)

Як читати ці варіанти:

  • (binaries: ...) друкується, якщо source-пакет із правила встановлений під іншими binary-іменами. Це саме ті пакети, які треба оновити: package glibc (binaries: libc-bin, libc6, locales) 2.35-0ubuntu3.15.
  • Кілька імен через кому означають пакети без source-пакета (наприклад, RPM) в однаковому стані й однієї версії: package kernel-tools, kernel-tools-libs 0:6.12.0-211.7.3.el10_2. Пакети з (binaries: ...) не об'єднуються.
  • [leftover] означає, що вразлива лише старіша встановлена версія і її ніхто не використовує (умови). У binaries такої частини входять лише пакети цієї версії.
  • (build <build_version>) друкується на Ubuntu, якщо порівнювалась повна версія збірки.

Один збіг

Якщо знайдено рівно одне правило, друкується один рядок:

VULNERABLE: CVE-2026-31431 on ubuntu focal: kernel 5.4.0-92 [priority: critical, known exploited]

Кілька збігів

VULNERABLE: <unique CVE count> unique CVEs found from <matching rule count> matching CVE rules
- <рядок першої CVE>
- <рядок другої CVE>
- <до поточного ліміту>
...

Приклад рядка CVE з кількома частинами:

- CVE-2026-43284 (dirtyfrag) on almalinux 10: kernel package 0:6.12.0-211.7.3.el10_2 for kernel 6.12.0-211.7.3.el10_2.x86_64; package kernel-tools, kernel-tools-libs 0:6.12.0-211.7.3.el10_2 [priority: critical]

Сортування і ліміти

  • Типово показується максимум 5 CVE; --cve-limit змінює ліміт, --show-all-cves знімає його і прибирає .... Ліміт рахує рядки CVE, а не правила.
  • Якщо CVE більше за поточний ліміт, у кінці списку додається ....
  • Дублікати прибираються: unique CVEs рахує унікальні CVE ID, matching CVE rules — rule hits після дедуплікації однакових частин у межах CVE.
  • Без enrichment kernel-збіги пріоритизуються над package-збігами.
  • З enrichment збіги спочатку сортуються за priority, а вже потім kernel вище package.
  • CVE з is_manual = true сортуються вище за звичайний critical.
  • CVE, у яких усі частини мають [leftover], завжди йдуть після решти, незалежно від priority. Якщо в CVE є й інші частини, рядок сортується за ними.

Кеш і fallback

Останній успішно завантажений рулсет зберігається у --cache-path. Якщо свіжий fetch не вдався:

  • поки кеш молодший за ttl рулсету — агент використовує кеш;
  • якщо кеш протух і --allow-expired-cache не задано — агент повертає ERROR (exit code 1): «не зміг перевірити» свідомо не маскується під OK.

Перевірка fallback-у вручну: один раз успішно запустіть агента, зробіть static URL недоступним і запустіть повторно з тим самим --cache-path — поки кеш свіжий, агент використає його.

Контрольні запуски

Override-и platform/release/kernel дозволяють перевірити зв'язку builder → hosting → agent з будь-якої машини:

local-agent --rules-base-url https://host/rules \
  --platform ubuntu --release focal --kernel-version 5.4.0-92

Очікування: VULNERABLE: CVE-2026-31431 on ubuntu focal: kernel 5.4.0-92, exit code 2 (або вищий, якщо для цього CVE в cve-priority.json заданий high/critical).

Негативний кейс:

local-agent --rules-base-url https://host/rules \
  --platform ubuntu --release focal --kernel-version 5.4.0-101

Очікування: OK: no matching CVE rules found, exit code 0.

Тести

python3 -m unittest discover -s tests -p 'test_*.py'
# або: uv run python3 -m unittest discover -s tests -p 'test_*.py'

Поточні обмеження

  • Агент очікує per-release static JSON-файли; --rules-base-url вказує на каталог, а не на окремий файл. RPM-платформи додають platform prefix до числового release (almalinux10.json).
  • У контейнері kernel_version-перевірка фактично стосується ядра хоста.
  • Debian kernel-правила порівнюють версію пакета, з якого зібране запущене ядро (uname -v), а за її відсутності — версію встановленого linux-image-*; uname -r із tracker version напряму не порівнюється.
  • AlmaLinux kernel-правила порівнюють RPM EVR пакета, якому належить каталог модулів запущеного ядра.
  • Зворотної телеметрії/звітування на сервер немає.
  • Сторонні репозиторії не покриваються. Правила описують лише пакети дистрибутива. Деталі — в Data Sources.