XML-файлы декодеров Wazuh определяют, как необработанные строки логов преобразуются в структурированные события безопасности. Ошибка в конфигурации декодера - отсутствующий элемент <order>, ссылка на несуществующий родительский декодер или несоответствие количества групп захвата в regex - может привести к тому, что критически важные поля будут молча отброшены из алертов, создавая слепые зоны в вашем SIEM-конвейере. Ручная проверка кода выявляет некоторые из этих проблем, но не масштабируется на сотни файлов декодеров, поставляемых с Wazuh или поддерживаемых вашей организацией.
В этой статье представлен wazuh-decoder-linter - инструмент статического анализа с открытым исходным кодом, который автоматически валидирует XML-файлы декодеров Wazuh. Он проверяет структуру, согласованность regex/order, атрибуты элементов и межфайловые цепочки родительских декодеров. Опционально инструмент интегрируется с Wazuh logtest API для верификации декодеров на работающем экземпляре менеджера. Из этого руководства вы узнаете, как установить инструмент, какие правила валидации он применяет, как использовать его из командной строки и Python-кода, а также как интегрировать его в CI/CD-конвейеры.
Структура XML-файлов декодеров Wazuh
Декодеры Wazuh определяются в XML-файлах, расположенных в /var/ossec/etc/decoders/ (пользовательские) или /var/ossec/ruleset/decoders/ (стандартные). Каждый файл содержит один или несколько элементов <decoder>. Типичный декодер имеет два уровня: родительский декодер, который сопоставляет имя программы, и дочерний декодер, который извлекает поля с помощью регулярного выражения.
<decoder name="example">
<program_name>^example</program_name>
</decoder>
<decoder name="example">
<parent>example</parent>
<regex>User '(\w+)' logged from '(\d+.\d+.\d+.\d+)'</regex>
<order>user, srcip</order>
</decoder>
Схема декодеров Wazuh поддерживает 16 дочерних элементов внутри <decoder>: parent, prematch, program_name, regex, order, fts, ftscomment, plugin_decoder, accumulate, type, json_null_field, json_array_structure, var, use_own_name, match и description. Каждый элемент имеет определённые ограничения: <regex> требует соответствующего <order>, <plugin_decoder> должен ссылаться на один из пяти известных плагинов (JSON_Decoder, OSSECAlert_Decoder, PF_Decoder, SymantecWS_Decoder, SonicWall_Decoder), а <type> принимает только восемь значений (firewall, ids, web-log, syslog, squid, windows, host-information, ossec).
Понимание этой структуры необходимо для написания корректных декодеров.
Типичные ошибки конфигурации декодеров
Анализ реальных файлов декодеров - включая более 80 файлов из стандартного набора правил Wazuh - выявляет несколько повторяющихся паттернов ошибок:
Regex без order (и наоборот). Наиболее частая ошибка. Элемент <regex> захватывает группы, но элемент <order>, который сопоставляет эти группы с именами полей, отсутствует. Декодер молча отбрасывает захваченные данные. Обратная ситуация - <order> без <regex> - также возникает, когда нет <plugin_decoder> для предоставления полей.
Несоответствие групп захвата. Регулярное выражение с 2 группами захвата в паре с <order>, содержащим 3 поля. Третье поле никогда не заполняется. Эта проблема коварна тем, что Wazuh не выдаёт ошибку при выполнении - он просто оставляет поле пустым.
Осиротевшие ссылки на родителей. Дочерний декодер объявляет <parent>sshd-custom</parent>, но ни один декодер с именем sshd-custom не существует в загруженных файлах. Дочерний декодер никогда не активируется.
Некорректные атрибуты элементов. Атрибут offset элемента <prematch> принимает только after_regex и after_parent. Использование after_prematch для <prematch> (допустимо только для <regex>) приводит к неопределённому поведению. Аналогично, <regex type="osmatch"> некорректен, поскольку osmatch не поддерживает группы захвата.
Нарушения синтаксиса OS_Regex. Стандартный движок регулярных выражений Wazuh (osregex) не поддерживает конструкции (?...), квантификаторы {n,m} и альтернацию (|) внутри групп. Использование этих паттернов приводит к молчаливым сбоям сопоставления.
Пустые элементы plugin_decoder. <plugin_decoder></plugin_decoder> без содержимого - всегда ошибка. Элемент должен указывать, какой плагин вызывать.
Архитектура wazuh-decoder-linter
Инструмент написан на Python (3.10+) с использованием lxml для парсинга XML и click для CLI. Кодовая база следует модульной архитектуре:
wazuh_decoder_linter/
cli.py # Click CLI: разбор аргументов, форматирование вывода
constants.py # Все константы валидации, перечисления, допустимые множества
linter.py # Ядро: класс WazuhDecoderLinter
logtest.py # Интеграция с Wazuh logtest API
models.py # Модели данных: LintResult, LintReport, DecoderMeta
regex_utils.py # Подсчёт групп regex и валидация синтаксиса
Ядро (WazuhDecoderLinter) использует двухпроходную стратегию парсинга для обеспечения устойчивости. Сначала выполняется попытка разобрать весь файл как XML. При неудаче - что часто случается, когда один блок декодера содержит некорректный контент - инструмент переключается на извлечение отдельных блоков <decoder>...</decoder> с отслеживанием глубины вложенности и обрабатывает каждый независимо. Это означает, что один некорректный декодер не препятствует проверке остальных валидных декодеров в том же файле.
Санитизация XML обрабатывает специфичные для Wazuh паттерны, которые нарушают стандартный парсинг XML: неэкранированные символы &, границы слов OS_Regex \< и голые символы <, не являющиеся XML-тегами. Санитайзер сохраняет валидные XML-сущности (&, {, ), экранируя всё остальное.
16 правил валидации
Линтер реализует 16 отдельных правил валидации, каждое из которых привязано к уровню серьёзности (ERROR, WARNING или INFO):
Ошибки (обязательно исправить)
| Правило | Описание |
|---|---|
| Атрибут name | Каждый <decoder> должен иметь атрибут name |
| Пустой parent | Элементы <parent> не должны быть пустыми |
| Согласованность regex/order | <regex> требует <order> и наоборот (если нет <plugin_decoder>) |
| Формат полей order | Имена полей должны соответствовать известным статическим полям или динамическому паттерну ^\w[\w.\- ]*$ |
| Некорректные offset | Атрибуты offset должны использовать допустимые значения для каждого типа элемента |
| Некорректные типы regex | Атрибут type должен быть osregex, pcre2 или osmatch |
| Пустой plugin_decoder | <plugin_decoder> не должен быть пустым |
| Некорректные JSON-поля | <json_null_field> должен быть “string” или “discard”; <json_array_structure> - “array” или “csv” |
| Значение use_own_name | Должно быть “true” |
Предупреждения (рекомендуется исправить)
| Правило | Описание |
|---|---|
| Неизвестные элементы | XML-элементы, не входящие в 16 известных дочерних элементов декодера |
| Несоответствие групп | Количество групп захвата regex не совпадает с количеством полей order |
| Тип декодера | Значение <type> не входит в 8 распознаваемых типов |
| Неизвестный плагин | Имя plugin_decoder не входит в 5 известных плагинов |
| use_own_name без parent | <use_own_name> требует <parent> |
| Accumulate без ID | <accumulate> требует “id” в полях <order> |
| Поля FTS | Поля FTS должны соответствовать известным статическим полям плюс location и name |
| Синтаксис OS_Regex | Выявление неподдерживаемых конструкций в паттернах osregex |
| Цепочка родителей | Межфайловая: имена родительских декодеров должны существовать в сканируемых файлах |
Модуль утилит для регулярных выражений заслуживает отдельного внимания. Он различает Wazuh OS_Regex и PCRE2 при подсчёте групп захвата. В OS_Regex \( - это литеральная скобка (не группа), тогда как в PCRE2 незахватывающие группы ((?:...)), опережающие проверки ((?=...)) и ретроспективные проверки ((?<=...)) исключаются из подсчёта. Такая точность предотвращает ложные срабатывания при валидации количества групп.
Установка и использование CLI
Установка непосредственно из GitHub:
# Базовая установка
pip install git+https://github.com/pyToshka/wazuh-linter.git
# С поддержкой API-тестирования
pip install "wazuh-decoder-linter[api] @ git+https://github.com/pyToshka/wazuh-linter.git"
Статический анализ
Проверка отдельного файла или целой директории:
# Проверка отдельного файла
wazuh-decoder-lint /var/ossec/etc/decoders/local_decoder.xml
# Проверка всех файлов декодеров в директории
wazuh-decoder-lint /var/ossec/etc/decoders/
# Строгий режим: предупреждения считаются ошибками
wazuh-decoder-lint --strict /var/ossec/etc/decoders/
# Показать INFO-сообщения
wazuh-decoder-lint --show-info /var/ossec/etc/decoders/
# JSON-вывод для интеграции с CI/CD
wazuh-decoder-lint --format json /var/ossec/etc/decoders/
API-тестирование на работающем менеджере
Инструмент может верифицировать декодеры на работающем менеджере Wazuh через logtest API:
# Инлайн тестовый лог
wazuh-decoder-lint /var/ossec/etc/decoders/ \
--test-api \
--api-url https://wazuh-manager:55000 \
--api-user wazuh-wui \
--test-log "sshd:Oct 15 21:07:00 myhost sshd[1234]: Failed password for root from 10.0.0.1"
# YAML-файл с тестовыми случаями
wazuh-decoder-lint /var/ossec/etc/decoders/ \
--test-api \
--test-file test_cases.yml
Формат YAML-файла с тестами:
tests:
- event: 'Oct 15 21:07:00 myhost sshd[1234]: Failed password for root from 10.0.0.1'
decoder: sshd
description: 'SSH failed password'
- event: '192.168.1.1 - - [15/Oct/2024:21:07:00 +0000] "GET / HTTP/1.1" 200 1234'
decoder: web-accesslog
description: 'Apache access log entry'
Для безопасности API-пароля используйте переменную окружения WAZUH_API_PASSWORD вместо флага --api-pass, чтобы избежать раскрытия учётных данных в списке процессов.
Коды возврата
| Код | Значение |
|---|---|
| 0 | Все проверки пройдены |
| 1 | Обнаружены ошибки (или предупреждения в строгом режиме, или неудачные API-тесты) |
| 2 | Ошибка использования CLI |
Программный Python API
Инструмент предоставляет Python API для интеграции в собственные инструменты:
from wazuh_decoder_linter import WazuhDecoderLinter
linter = WazuhDecoderLinter()
report = linter.lint_paths(["path/to/decoders/"])
for result in report.results:
print(f"[{result.severity}] {result.file}:{result.line} - {result.message}")
if report.has_errors:
print(f"Found {len(report.errors)} error(s)")
# Строгий режим: предупреждения считаются сбоями
if report.has_failures(strict=True):
sys.exit(1)
Для API-тестирования:
from wazuh_decoder_linter import WazuhLogtest
with WazuhLogtest(
api_url="https://wazuh:55000",
user="wazuh-wui",
password="secret",
verify_ssl=False,
) as tester:
results = tester.test_batch([
{"event": "Failed password for root from 10.0.0.1", "decoder": "sshd"},
])
for result in results:
print(f"[{result['status']}] {result['description']}")
Интеграция с CI/CD-конвейерами
GitHub Actions
name: Lint Wazuh Decoders
on:
push:
paths:
- 'decoders/**'
pull_request:
paths:
- 'decoders/**'
jobs:
lint-decoders:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install wazuh-decoder-linter
run: pip install "wazuh-decoder-linter[api] @ git+https://github.com/pyToshka/wazuh-linter.git"
- name: Lint decoders
run: wazuh-decoder-lint --strict --format json decoders/ > lint-results.json
- name: Upload results
if: always()
uses: actions/upload-artifact@v4
with:
name: lint-results
path: lint-results.json
Pre-commit хук
Репозиторий включает .pre-commit-config.yaml, обеспечивающий качество кода с помощью black, isort, flake8, bandit и mypy. Вы можете добавить проверку декодеров как локальный pre-commit хук:
repos:
- repo: local
hooks:
- id: wazuh-decoder-lint
name: Wazuh Decoder Lint
entry: wazuh-decoder-lint --strict
language: python
files: '\.xml$'
types: [file]
Формат вывода JSON интегрируется с любой CI/CD-системой, способной обрабатывать структурированный вывод. Конвенция кодов возврата (0 = успех, 1 = ошибка, 2 = ошибка использования) следует стандартным Unix-соглашениям для бесшовной интеграции в конвейеры.
Для аналогичного подхода к автоматизированной валидации безопасности в контейнерных средах см. Безопасность образов контейнеров с Wazuh и Trivy.
Тестирование на реальных декодерах
Проект включает обширный набор тестов: более 80 валидных XML-файлов декодеров из стандартного набора правил Wazuh, более 80 намеренно испорченных копий для тестирования обнаружения ошибок и более 90 параметризованных интеграционных тестовых случаев, охватывающих декодеры SSH, Apache, Cisco, Fortinet, Snort, auditd, Windows Security, AWS, Docker, Kubernetes и десятков других источников.
Пример текстового вывода:
[ERROR] broken_decoder.xml:14 - Empty <plugin_decoder> in decoder 'json-msgraph'
[WARNING] decoder.xml:58 - Decoder 'sshd-success': regex (osregex) has 2 capture
group(s) but order has 3 field(s)
============================================================
Wazuh Logtest API Results
============================================================
[PASS] SSH failed password
[FAIL] Apache access log
expected: apache, got: web-accesslog
API tests: 1 passed, 1 failed, 0 errors
Summary: 1 error(s), 1 warning(s)
Интеграционный набор тестов разворачивает полный стек Wazuh (менеджер, индексатор, панель управления) через Docker Compose и выполняет все 90+ тестовых случаев на реальном logtest API. Это подтверждает, что результаты статического анализа линтера соответствуют фактическому поведению Wazuh при выполнении.
Заключение
Статический анализ XML-файлов декодеров Wazuh устраняет разрыв между написанием декодеров и их развёртыванием с уверенностью в корректности. Инструмент wazuh-decoder-linter обнаруживает ошибки конфигурации - отсутствующие элементы order, несоответствия групп захвата regex, осиротевшие цепочки родителей, некорректные атрибуты - до того, как они попадут в продакшен и вызовут молчаливую потерю данных в вашем SIEM-конвейере.
Инструмент доступен как открытый исходный код под лицензией BSD 3-Clause, поддерживает Python 3.10+ и размещён на github.com/pyToshka/wazuh-linter. Приглашаем к участию в разработке правил валидации, тестовых случаев и документации.
Дополнительные материалы
- Безопасность образов контейнеров с Wazuh и Trivy - Автоматизированная валидация безопасности с Wazuh
- RAG для документации Wazuh: Часть 1 - Построение систем извлечения информации из базы знаний Wazuh
- Wazuh LLM: Llama 3.1 для анализа событий безопасности - ИИ-модель для анализа событий безопасности Wazuh