
Открытый сканер CVE с одним скриптом для парков управляемых через RMM. Чистый PowerShell 7 — сопоставляет инвентарь программного обеспечения вашего RMM с NVD, CISA KEV, EPSS и SSVC, чтобы ответить: уязвима ли эта версия и насколько это срочно?
Сканер CVE с открытым исходным кодом для флотов, управляемых через RMM. Один скрипт на чистом PowerShell 7, без агентов, без серверных компонентов, без лицензионных отчислений.
Он отвечает на один вопрос для каждого программного обеспечения на каждом управляемом устройстве: известно ли, что установленная версия уязвима, и насколько это срочно? — объединяя инвентаризацию ПО вашего RMM с бесплатными авторитетными источниками безопасности:
Результаты: отчёт CSV по каждому устройству, история SQLite с часами SLA и событиями изменений неделя к неделе, самодостаточная HTML-панель управления и опциональный отчёт об уязвимостях в формате Markdown.
v1.0.0. Движок был перенесён «изолированно» со сканера, который еженедельно выполнялся в продакшене для Windows-флота (см. docs/HISTORY.md). Перед этим релизом оба сканера запускались на одном и том же живом флоте и сравнивались построчно: по десяткам тысяч находок, где оба видели одинаковую тройку (устройство, ПО, версия), каждое вычисленное поле — статус, CVSS, уровень серьёзности, флаг KEV, решение SSVC, версия исправления — полностью совпало.
Это сравнение покрывает логику вердикта. Оно не тестировало слой истории/SLA и не заменяет проверку инструмента в вашем окружении. Прочтите docs/known-limitations.md, прежде чем доверять ему — он написан честно, а не лестно.
Вам понадобится PowerShell 7 (pwsh). Два способа запуска:
cp config.example.json config.json
# отредактируйте config.json: укажите ninjaone.client_id / client_secret / base_url,
# установите output.report_dir и (рекомендуется) nvd.api_key
pwsh -File fleet-cve-scan.ps1
Экспортируйте инвентаризацию в CSV с колонками hostname, software, version (опционально device_id, os), затем:
cp config.example.json config.json # здесь нужен только output.report_dir
pwsh -File fleet-cve-scan.ps1 -InputCsv inventory.csv
-InputCsv не выполняет ни одного вызова NinjaOne — никакого OAuth, никакого API. Он всё равно запрашивает NVD и другие публичные каналы, поэтому вам также понадобится nvd.api_key в config.json и корректный output.report_dir. См. docs/rmm-adapters.md для описания контракта входных данных и инструкций по экспорту для каждого RMM.
brew install powershell; Windows: winget install Microsoft.PowerShell; Debian/Ubuntu: установка из репозитория Microsoft (packages.microsoft.com).brew install sqlite; Debian/Ubuntu: apt-get install sqlite3; Windows: скачайте пакет «tools» SQLite с sqlite.org и поместите sqlite3.exe в PATH или рядом со скриптом. Без него сканирование всё равно выполняется; first_seen будет установлен на дату запуска.first_seen для SLA. Отсутствует = эти функции пропускаются, сканирование завершается.epss.empiricalsecurity.com (хост массовых оценок EPSS — FIRST переместил хостинг данных EPSS туда), MSRC, endoflife.date и GitHub raw (CVE Program cvelistV5) — плюс API NinjaOne в живом режиме.Конфигурация — это JSON-файл (по умолчанию config.json рядом со скриптом; можно переопределить с помощью -ConfigPath). Начните с config.example.json. Все ключи, которые читает сканер, значения по умолчанию при их отсутствии и что они делают:
ninjaone (живой режим)nvd| Ключ | По умолчанию | Назначение |
|---|---|---|
nvd.api_key | "" | API-ключ NVD, отправляется как заголовок apiKey. Пустое значение — неаутентифицированный доступ, который понижает значения по умолчанию для nvd_rate_limit и nvd_min_spacing_ms до анонимных лимитов NVD (4/30с, 6500 мс) вместо аутентифицированных (48/30с, 700 мс). Бесплатный и ~в 10 раз быстрее. |
Значения nvd_min_spacing_ms, nvd_rate_limit и parallel_throttle считываются сканером, но отсутствуют в config.example.json — добавляйте их только если нужно настроить. См. docs/rate-limiting.md.
outputВсё перечисленное попадает в output.report_dir.
vuln-report-YYYY-MM-DD.csv — отчёт по каждой находке. 28 столбцов (схема ниже).vuln-history.db — история в SQLite (требует sqlite3): runs (сводки по запускам), findings (история открытых находок по (device, software, cve) с first_seen / last_seen / resolved_date / epss_score) и changes (типизированные события неделя к неделе).cve-dashboard.html — самодостаточная HTML-панель (библиотека графиков встроена, внешних запросов нет). Опционально копируется в output.dashboard_share.Набор столбцов фиксирован и контролируется проверкой при выпуске. По порядку:
Run-WeeklyScan.ps1 — это обёртка для запланированных запусков: он запускает сканер из своего каталога, захватывает все выходные потоки в файл с датой в <каталог скрипта>/logs/ (переопределяется через -LogDir) и передаёт код возврата сканера, чтобы планировщик видел сбои.
Планировщик задач Windows:
schtasks /Create /TN "CVE-Scan-Weekly" ^
/TR "pwsh -NoProfile -File C:\путь\к\Run-WeeklyScan.ps1" ^
/SC WEEKLY /D SUN /ST 22:00 /RU SYSTEM
cron (Linux/macOS):
0 22 * * 0 pwsh -File /путь/к/Run-WeeklyScan.ps1
cve-dashboard.html полностью самодостаточен — открывайте его напрямую, размещайте на любом статическом хосте или файловом общем ресурсе, либо автоматически копируйте после каждого запуска через output.dashboard_share. Для размещения с контролем доступа в каталоге dashboard-auth/ находится минимальный хост ASP.NET с аутентификацией Microsoft Entra (Azure AD); смотрите его README и RUNBOOK.
-InputCsv позволяет использовать экспорт из любого RMM/SCCM/Intune, но только адаптер NinjaOne работал в продакшене.Полный, безжалостный список — процент пропуска CPE, маскировка ключа дедупликации, totalResults только первой страницы, пропуски в структуре замещения MSRC и другое — в docs/known-limitations.md.
Несколько структурных решений, которые выглядят нестандартно, сделаны намеренно:
ForEach-Object -Parallel, и эти пространства выполнения не видят функции внешней области видимости — поэтому несколько вспомогательных функций намеренно продублированы внутри параллельного блока. Расположение «одна функция на файл» в модуле не устранило бы это дублирование (параллельный блок всё равно требовал бы внедрения исходного кода в каждое пространство выполнения); это просто добавило бы шаг сборки и нарушило модель развёртывания «скопировал один файл». Если проект когда-нибудь перерастёт однофайловую структуру, упаковка будет изменением в v2, а не патчем.[PSCustomObject], а не классы. Записи находок создаются внутри параллельных пространств выполнения и передаются обратно в основное пространство. Экземпляры классов PowerShell не всегда надёжно проходят через эту границу; [PSCustomObject] — да, а контракт столбцов CSV обеспечивается проверкой при выпуске, а не системой типов.tests/ — это скрипты без зависимостей с проверкой кода возврата, которые можно запускать везде, где есть pwsh, включая сервер сканирования, без установки чего-либо. CI запускает их все на Windows и Ubuntu.Принимаются Pull Request’ы — особенно для адаптеров RMM. См. CONTRIBUTING.md для базовых правил и требования «безопасного направления» (логика статуса может ошибаться только в сторону большей видимости, никогда молча в сторону PATCHED).
Этот инструмент полезен настолько, насколько полезны бесплатные авторитетные каналы, с которыми он работает. Каждый из перечисленных запрашивается во время сканирования; пожалуйста, соблюдайте их условия, если вы строите что-то на их основе.
cvelistV5 — записи CVE плюс данные SSVC / vulnrichment от CISA ADP. CVE® — зарегистрированный товарный знак The MITRE Corporation. https://github.com/CVEProject/cvelistV5Apache-2.0 — см. LICENSE.
| Ключ | По умолчанию | Назначение |
|---|
ninjaone.client_id | "" | OAuth2-идентификатор клиента API NinjaOne. Обязателен для живого сканирования; оставьте пустым для -InputCsv. |
ninjaone.client_secret | "" | Секрет клиента API NinjaOne. |
ninjaone.base_url | — (в примере указано https://app.ninjarmm.com) | Базовый URL API NinjaOne; обязателен для живого сканирования, встроенного запасного варианта нет. Используйте хост вашего региона (например, eu.ninjarmm.com, oc.ninjarmm.com). |
| Ключ | По умолчанию | Назначение |
|---|
cvss_threshold | 7.0 | Минимальная базовая оценка CVSS для того, чтобы CVE с оценкой считался УЯЗВИМЫМ. CVE из списка KEV обходят этот порог. |
ssvc_mission_prevalence | high | Входные данные заинтересованной стороны для Mission Prevalence в SSVC (low / medium / high). |
ssvc_public_wellbeing | high | Входные данные заинтересованной стороны для Public Well-being в SSVC (low / medium / high). |
| Ключ | По умолчанию | Назначение |
|---|
nvd_cache_ttl_days | 7 | Дни, в течение которых результат NVD (на одно имя ПО) хранится в кэше перед повторным запросом. |
nvd_cache_flush_every | 500 | Сохранять кэш NVD на диск каждые N обработанных элементов, чтобы прерванное сканирование сохранило загруженные данные. 0 отключает (сохранение по окончании сканирования всё равно выполняется). |
nvd_min_spacing_ms | 700 с API-ключом, 6500 без | Минимальное количество миллисекунд между вызовами NVD. Основное ограничение скорости — настраивайте его в первую очередь. |
nvd_rate_limit | 48 с API-ключом, 4 без | Ограничение по скользящему окну на количество вызовов NVD за 30 секунд (запасной лимит). Минимум 1. |
parallel_throttle | 20 | Количество пространств выполнения в ForEach-Object -Parallel. Вызовы NVD в любом случае последовательны; это ограничивает только параллелизм при попадании в кэш. Минимум 1. |
kev_cache_ttl_hours | 24 | Время жизни кэша каталога CISA KEV. |
epss_cache_ttl_hours | 24 | Время жизни кэша файла оценок EPSS от FIRST.org. |
eol_cache_ttl_days | 7 | Время жизни кэша запросов к endoflife.date. |
msrc_cache_ttl_days | 30 | Время жизни кэша данных MSRC CVRF. |
cvelist_cache_ttl_days | 7 | Время жизни кэша данных CVE Program (cvelistV5) / CISA-ADP SSVC. |
cvelist_fetch_budget | 300 | Максимальное количество загрузок записей CVE за один запуск для обогащения SSVC/vulnrichment; остальные откладываются до следующего запуска. |
| Ключ | По умолчанию | Назначение |
|---|
epss_spike_threshold | 0.10 | Минимальный рост оценки EPSS у находки между сканированиями, чтобы создать событие изменения EPSS_SPIKE. |
sla_days_critical | 14 | Окно SLA (дней с first_seen) для находок CRITICAL. |
sla_days_high | 30 | Окно SLA для других оценок серьёзности. |
denied_software_csv | "" | Путь к опциональному CSV-файлу с запрещённым ПО для перекрёстной проверки. Пустое значение отключает проверку. |
| Ключ | По умолчанию | Назначение |
|---|
output.report_dir | (обязательно) | Каталог для всех выходных данных — CSV, базы истории, панели управления и кэшей. |
output.wiki_security_page | "" | Опциональный путь к странице уязвимостей в формате Markdown. Записывается только при указании. |
output.dashboard_share | "" | Опциональный путь/общий ресурс, куда копируется панель управления после каждого запуска. Пустое значение = только локально. |
eol-report-YYYY-MM-DD.csv — записывается при обнаружении семейств ПО, достигших конца поддержки.denied-report-YYYY-MM-DD.csv — записывается, если настроен denied_software_csv и есть совпадения.nvd-cache.json, kev-cache.json, epss-cache.json, eol-cache.json, msrc-cache.json, cvelist-cache.json (все игнорируются git).output.wiki_security_page, только если задан.| # | Колонка | Значение |
|---|
| 1 | device_id | Идентификатор устройства в RMM (или детерминированный синтетический идентификатор в режиме -InputCsv). |
| 2 | hostname | Имя устройства. |
| 3 | os | Строка операционной системы (может быть пустой). |
| 4 | software | Название продукта по данным инвентаризации. |
| 5 | installed_version | Строка версии по данным инвентаризации. |
| 6 | cve_id | Идентификатор CVE. |
| 7 | cvss_score | Базовая оценка CVSS (0, если нет). |
| 8 | severity | Метка уровня серьёзности CVSS. |
| 9 | cvss_version | Какая версия CVSS использовалась (4.0/3.1/3.0/2.0/пусто). |
| 10 | epss_score | Вероятность EPSS от FIRST.org (пусто, если CVE отсутствует в каталоге). |
| 11 | epss_percentile | Процентиль EPSS (пусто, если отсутствует). |
| 12 | ssvc_exploitation | Сырой параметр эксплуатации CISA SSVC (none/poc/active; пусто без данных ADP). |
| 13 | ssvc_decision | Решение SSVC: Act/Attend/Track (пусто без данных ADP). |
| 14 | status | VULNERABLE / PATCHED / UNKNOWN. |
| 15 | published_date | Дата публикации CVE. |
| 16 | unknown_reason | Почему строка имеет статус UNKNOWN (SUSPECT_VERSION / NO_VERSION_BOUND / VERSION_PARSE_ERROR; пусто в остальных случаях). |
| 17 | is_kev | True, если CVE есть в каталоге CISA KEV. |
| 18 | kev_due_date | Дата устранения по CISA KEV. |
| 19 | kev_ransomware_use | Флаг использования в кампаниях программ-вымогателей KEV. |
| 20 | fix_version | Вычисленная граничная версия исправления. |
| 21 | fix_boundary_inclusive | Является ли граница исправления включительной. |
| 22 | advisory_url | URL-адрес уведомления поставщика или подробностей NVD. |
| 23 | patch_evidence | Свидетельство, когда Microsoft UNKNOWN был переключён на PATCHED. |
| 24 | triage_status | Статус триажа из triage.json (пусто, если нет). |
| 25 | triage_note | Заметка триажа. |
| 26 | first_seen | Дата первого обнаружения находки (из базы истории). |
| 27 | sla_due | Срок выполнения SLA. |
| 28 | sla_breach | Превышен ли SLA. |