
Wazuh Schema Validator API 详解C 与 C 双接口下的 JSON 消息模式校验【免费下载链接】wazuhWazuh - The Open Source Security Platform. Unified XDR and SIEM protection for endpoints and cloud workloads.项目地址: https://gitcode.com/GitHub_Trending/wa/wazuh本篇技术指南以 Wazuh 仓库中的 Schema Validator API 参考文档为核心系统讲解该共享模块的两套对外接口——C 的SchemaValidatorFactory/ISchemaValidatorEngine抽象体系与面向 C 模块如 FIM的schema_validator_*系列函数并结合src/shared_modules/schema_validator/下的真实源码说明每个方法的底层实现、支持的索引模板类型、校验规则细节strict 模式、null 容错、数组逐元素校验、三种典型的模块集成模式以及单元测试中的依赖注入技巧。读完后你将能够直接在任何 Wazuh 组件中正确接入模式校验并理解校验失败时本地库删除防一致性循环这一设计决策的来源。模块定位在数据进入 Wazuh-indexer 前做最后一道关卡Schema Validator 是 Wazuh 的共享模块位于 src/shared_modules/schema_validator职责是将即将发送到索引器的 JSON 消息对照 Wazuh-indexer 索引模板 mapping 进行本地校验防止因类型不匹配、字段缺失或未知字段导致的索引报错从而保障 FIM、SCA、Syscollector 等组件写入数据的一致性。其核心设计特征均可在仓库中确认模式文件编译期嵌入CMake 构建时自动将 JSON 模板烧入二进制运行时零配置单例工厂 接口抽象SchemaValidatorFactory管理多个索引的校验器实例模块通过ISchemaValidatorEngine接口操作便于测试时注入 mockC/C 双接口C 模块Syscollector、SCA直接用类接口C 模块syscheckd/FIM通过extern C封装函数调用线程安全工厂内部使用std::shared_mutex保护共享状态见 schemaValidator.cpp 中的注释工厂被 syscollector、sca 等多个模块在同一进程内并发初始化读写必须加锁。嵌入机制本身由 CMakeLists.txt 实现CMake 在配置阶段扫描 schema 目录下的*.json并显式排除metrics-*.json——这类模板仅由 Python framework 在运行时使用不应嵌入 C 库生成一个schemaResources.cpp把每个 JSON 压缩为 raw string literal 存入std::mapstd::string, std::string由Resources::getEmbeddedSchemas()schemaResources.hpp对外提供。C API 全景头文件 schemaValidator.hpp 在SchemaValidator命名空间下定义了四个核心构件ValidationResult结构体、ISchemaValidatorEngine抽象接口、SchemaValidatorEngine具体实现以及SchemaValidatorFactory单例工厂。SchemaValidatorFactory单例工厂工厂负责按索引模式index pattern到校验器实例的映射管理是整个模块的入口。getInstance()static SchemaValidatorFactory getInstance();返回单例引用。实现极其标准schemaValidator.cppSchemaValidatorFactory SchemaValidatorFactory::getInstance() { static SchemaValidatorFactory instance; return instance; }注意工厂的拷贝构造、赋值运算符均被delete私有构造/析构保证实例唯一性。initialize()bool initialize( std::mapstd::string, std::shared_ptrISchemaValidatorEngine customValidators {} );参数customValidators—— 可选的索引模式 → 校验器实例映射用于测试时依赖注入。返回初始化成功返回true否则false。用法示例auto factory SchemaValidator::SchemaValidatorFactory::getInstance(); if (factory.initialize()) { m_logFunction(LOG_INFO, Schema validator initialized); } else { m_logFunction(LOG_ERROR, Failed to initialize schema validator); }源码中的initialize()实现schemaValidator.cpp有三个值得注意的行为幂等性工厂是进程级单例会被多个模块syscollector、sca在启动时各自调用一次initialize()。一旦m_initialized为true直接返回成功而不重复加载——源码注释明确说明了这一设计动机注入优先若传入的customValidators非空则用它整体替换内嵌资源加载这是单测 mock 的通道默认路径从Resources::getEmbeddedSchemas()遍历所有嵌入的 schema 内容逐个解析入 map最终至少加载到一个校验器才算初始化成功。getValidator()std::shared_ptrISchemaValidatorEngine getValidator(const std::string indexPattern);参数indexPattern—— 索引模式名例如wazuh-states-inventory-packages、wazuh-states-fim-file。返回对应的校验器实例找不到时返回nullptr。auto validator factory.getValidator(wazuh-states-inventory-packages); if (validator) { // Use validator }实现上map 的 key 是 schema 解析出的index_patterns[0]去掉通配符*后的前缀见下文schema 解析规则因此查询时必须传去除通配符后的基础名。读操作持shared_lock可与并发读取共存。isInitialized()bool isInitialized() const;返回true表示已初始化false表示尚未初始化。if (factory.isInitialized()) { // Factory ready to use }reset()void reset();清空所有校验器并把初始化标志复位专为单元测试设计// For unit tests factory.reset(); factory.initialize(mockValidators);ISchemaValidatorEngine抽象校验接口该接口定义了所有校验器实现的公共契约schemaValidator.hpp具体实现类是SchemaValidatorEngine采用 pimpl 模式私有class Implstd::unique_ptrImpl持有状态。validate()string 重载virtual ValidationResult validate(const std::string message) 0;参数message—— JSON 消息字符串。返回ValidationResult含校验状态与错误列表。std::string json R({agent: {id: 001}}); auto result validator-validate(json); if (result.isValid) { // Valid } else { for (const auto error : result.errors) { m_logFunction(LOG_ERROR, error); } }注意 string 重载的容错边界SchemaValidatorEngine::validate(const std::string)schemaValidator.cpp会先nlohmann::json::parseJSON 本身解析失败时不抛异常而是返回isValidfalse且 errors 中有一条JSON parse error: ...。因此调用方无需额外 try/catch 即可拿到结构化的失败结果。validate()json 对象重载virtual ValidationResult validate(const nlohmann::json message) 0;参数message—— 已解析的nlohmann::json对象。适合调用方持有 JSON 对象时的零序列化开销校验。nlohmann::json json {{agent, {{id, 001}}}}; auto result validator-validate(json);getSchemaName()virtual std::string getSchemaName() const 0;返回schema 名称由索引模式推导而来。std::string name validator-getSchemaName(); // Returns: wazuh-states-inventory-packagesValidationResult校验结果结构体struct ValidationResult { bool isValid; // True if validation passed std::vectorstd::string errors; // List of validation errors (empty if valid) };源码schemaValidator.hpp中默认构造函数把isValid初始化为true——引擎实现先收集所有错误最后以result.isValid result.errors.empty()一次性定论即一次校验会尽力报告全部字段错误而不是遇到第一个错误就停止。遍历错误的通用写法auto result validator-validate(message); if (!result.isValid) { std::cerr Validation failed with result.errors.size() errors: std::endl; for (const auto error : result.errors) { std::cerr - error std::endl; } }schema 解析规则从索引模板到校验状态SchemaValidatorEngine::Impl::loadSchemaFromString()与parseAndInitializeSchema()schemaValidator.cpp定义了校验器从原始 JSON 模板中抽取什么、忽略什么必须包含template.mappings.properties否则加载失败。schema 的输入形态就是 Wazuh-indexer 的索引模板 JSON而非独立的 JSON Schemastrict 模式判定读取template.mappings.dynamic字段其值为strict时开启严格模式——消息中出现 mapping 未定义的字段会报错schema 名推导取index_patterns数组第一个元素若含*则截去通配符部分test-index*→test-index。这就是getSchemaName()的返回值也是工厂 map 的 key。理解了这三点就能解释工厂行为initializeFromEmbeddedResources()对每个嵌入文件调用loadSchemaFromString成功后以getSchemaName()为 key 存入 map。支持的 Wazuh-indexer 数据类型与校验细节文档声明校验器支持全部 Elasticsearch 系数据类型类型说明示例text全文检索字符串description: A long text...keyword精确值字符串status: activelong64 位有符号整数size: 1024integer32 位有符号整数count: 42short16 位有符号整数priority: 5byte8 位有符号整数level: 3double64 位浮点score: 98.5float32 位浮点ratio: 0.75boolean布尔值enabled: truedate日期/时间戳timestamp: 2024-01-13T10:00:00Zobject嵌套对象agent: {id: 001}ipIPv4/IPv6 地址ip: 192.168.1.1源码validateField()schemaValidator.cpp中若干与文档表格互补的关键实现细节null 通吃任何字段值为null直接放行OpenSearch allows null values for any field。这与索引器不索引 null 值的行为对齐strict 模式下的 null 例外未定义字段本身被拒绝但其值为null时放行schemaValidator.cpp理由同样是null 无需 mapping数组逐元素校验数组值不检查是否为数组而是按索引路径path[i]递归校验每个元素错误定位精确到元素下标日期双格式date类型既接受数字epoch 时间戳也接受 ISO8601 字符串后者用正则^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[-]\d{2}:\d{2})?)?$校验schemaValidator.cppIP 校验与 zone idip类型用inet_pton同时尝试 IPv4/IPv6对带 RFC 4007 zone 后缀的地址如fe80::1%eth0源码先剥离%之后的部分再校验——注释说明这是对索引器IpFieldMapper行为的镜像索引器忽略%之后的内容额外覆盖的类型源码还处理了match_only_text按字符串校验、unsigned_long、scaled_float按任意数字校验这些未出现在文档的类型表格中但单测test-index*模板里确实用到了它们字段可选语义mapping 中定义但消息中未出现的字段被跳过fields are optional by default即该模块不做 required 字段强制只做出现即合规。C API面向 FIM 等 C 模块的封装C 接口定义在 schemaValidator_c.h实现见 schemaValidator_c.cpp。全部函数用EXPORTED标记导出Windows 下为__declspec(dllexport)GCC 下为 visibility default因此 FIM 等模块可以链接共享库调用。初始化函数schema_validator_initialize()bool schema_validator_initialize(void);返回初始化成功true失败false。实现上只是调用 C 工厂的initialize()并吞掉所有异常if (schema_validator_initialize()) { minfo(Schema validator initialized successfully); } else { mwarn(Failed to initialize schema validator); }schema_validator_is_initialized()bool schema_validator_is_initialized(void);返回已初始化true否则false。if (schema_validator_is_initialized()) { // Proceed with validation }校验函数schema_validator_validate()bool schema_validator_validate( const char* index, const char* message, char** errorMessage );参数index—— 索引名schema 查找键如wazuh-states-fim-filemessage—— 待校验 JSON 字符串errorMessage—— 输出参数失败时指向由库内部分配的错误文本调用方必须free()。返回校验通过true失败false。char* errorMessage NULL; const char* index wazuh-states-fim-file; const char* message {\file\:{\path\:\/etc/passwd\,\size\:1024}}; if (!schema_validator_validate(index, message, errorMessage)) { // Validation failed if (errorMessage) { merror(Schema validation failed: %s, errorMessage); mdebug2(Raw event that failed: %s, message); free(errorMessage); } // Delete from database to prevent integrity loops delete_from_database(data); } else { // Validation passed send_to_sync_protocol(message); }源码中有两个决定性的行为差异schemaValidator_c.cpp调用方必须知晓工厂未初始化 → 返回true放行。注释写明这是backward compatibilityC 模块可能在工厂就绪前就开始工作此时不拦截任何消息该索引无对应校验器 → 返回false拒绝并设置错误信息No schema validator found for index。源码注释表明这是刻意的保守策略be restrictive and reject the message instead of letting it through unvalidated——宁可误拒也不放行未经验证的数据。此外函数开头对index/message做了空指针检查直接返回false并把*errorMessage预置为nullptr保证输出参数始终可安全判空。多条校验错误会以换行符拼接成单一字符串。三种典型集成模式文档归纳了 Wazuh 各模块实际采用的三种集成范式均可以直接照抄。模式一校验后入队Syscollector/SCAC 模块在把事件推给同步协议之前做一道闸口工厂未就绪或无对应校验器时跳过校验而非拦截bool validateAndQueue(const std::string data, const std::string index) { auto factory SchemaValidator::SchemaValidatorFactory::getInstance(); // Check if factory is initialized if (!factory.isInitialized()) { return true; // Skip validation if not available } // Get validator for index auto validator factory.getValidator(index); if (!validator) { return true; // No validator for this index } // Validate auto result validator-validate(data); if (!result.isValid) { // Log errors std::string errorMsg Validation failed for index: index . Errors:; for (const auto error : result.errors) { errorMsg \n - error; } m_logFunction(LOG_ERROR, errorMsg); m_logFunction(LOG_ERROR, Raw event: data); return false; } return true; }模式二延迟批量删除SCA校验失败的检查项先累积到容器批处理结束后统一从本地库删除避免在循环中途做数据库操作// Vector to accumulate failed items std::vectornlohmann::json failedChecks; // Process events for (const auto event : events) { bool validationPassed ValidateAndHandleStatefulMessage( event, context, checkData, failedChecks); if (validationPassed) { PushStateful(event, operation, version); } } // Batch delete failed items DeleteFailedChecksFromDB(failedChecks);模式三C 语言校验FIMFIM 使用OSList记录失败项以便延迟删除bool validate_and_persist(const char* index, const char* data, void* item_data) { if (!schema_validator_is_initialized()) { return true; // Skip validation } char* errorMessage NULL; if (!schema_validator_validate(index, data, errorMessage)) { // Validation failed if (errorMessage) { mdebug2(Validation failed: %s, errorMessage); mdebug2(Raw event: %s, data); free(errorMessage); } // Mark for deferred deletion if (failed_list item_data) { OSList_AddData(failed_list, item_data); } return false; } return true; }校验失败 → 从本地库删除这一动作在三种模式中出现背后逻辑是不一致的脏数据若留在本地数据库会持续触发同步协议重试形成死循环删除后由源端重新推送干净数据。仓库中 syscheckd 确有相关调用点例如 syscheck.c 与 run_check.c 引用了schema_validator接口。错误消息格式校验错误统一采用字段路径 期望/实际的文本格式Field field_path expected type expected_type, got actual_type Required field field_path is missing Field field_path is not defined in schema (strict mode)示例Field package.version expected type keyword, got object Required field package.name is missing Field package.unknown_field is not defined in schema (strict mode) Field file.size expected type long, got string需要说明的是文档给出的是格式约定当前源码validateField()产出的实际措辞与字段路径的拼接方式为path: Expected type, got actual_type with value: dumped_value例如嵌套路径package.version、数组下标list[0]均按path . field/path[i]规则生成而 strict 模式下的未定义字段错误为fieldPath: Field not allowed in strict mode。排查日志时应以源码实际措辞为准。线程安全语义SchemaValidatorFactory线程安全单例 内部std::shared_mutex写操作取独占锁getValidator/isInitialized取共享锁校验器实例加载 schema 后不可变多线程并发调用validate()安全C API 三个函数内部仅做只读查找与无状态解析同样线程安全。这一点被专门的并发测试用例覆盖schemaValidatorConcurrency_test.cpp 验证多模块并发initialize()不会在底层std::map上产生数据竞争。测试支持与依赖注入依赖注入 mock 校验器单测无需真实 schema 文件直接向工厂注入 mock// Create mock validator auto mockValidator std::make_sharedMockSchemaValidator(); // Inject into factory std::mapstd::string, std::shared_ptrISchemaValidatorEngine mocks; mocks[test-index] mockValidator; auto factory SchemaValidator::SchemaValidatorFactory::getInstance(); factory.reset(); factory.initialize(mocks); // Now getValidator() returns your mock测试间重置工厂// Reset factory state between tests SchemaValidator::SchemaValidatorFactory::getInstance().reset();仓库中的单测实践印证了这套机制schemaValidator_test.cpp 在SetUp()里用nlohmann::json内存构造了一个带index_patterns: [test-index*]、dynamic: strict的完整模板覆盖 keyword/integer/long/date/boolean/ip/object 等字段类型随后验证校验行为——同时它也示范了 schema 模板的完整 JSON 形态priority、template.settings、template.mappings三层结构。模块还提供独立的命令行调试工具 testtool/main.cpp用于手工验证某个消息对某索引的校验结果。构建与运行前提Schema Validator 作为schema_validator共享库编入主构建Windows 下产出.dllLinux 下设置-Wl,-rpath$ORIGIN便于相对路径加载make TARGETserver|agent DEBUG1运行单元测试cd src/build ctest -L schema_validator -V适用前提与限制小结模块无运行时外部依赖schema 已内嵌但新增索引模板需要重新构建才能被校验器识别C 接口的未初始化放行 / 无校验器拒绝两种非对称语义与 C 接口无校验器返回nullptr、由调用方自行决定放行或跳过的语义不同移植集成代码时不要想当然对齐校验器只做出现的字段是否合规 strict 模式下是否有多余字段不强制 required 字段日期按 ISO8601 正则做格式校验语义正确性如时区合法性深度校验不在本模块职责内。参考路径索引内容路径API 参考本文主体文档docs/ref/modules/utils/schema-validator/api-reference.md模块概览与集成状态docs/ref/modules/utils/schema-validator/README.md集成指南docs/ref/modules/utils/schema-validator/integration-guide.mdC 头文件src/shared_modules/schema_validator/include/schemaValidator.hppC 头文件src/shared_modules/schema_validator/include/schemaValidator_c.hC 核心实现src/shared_modules/schema_validator/src/schemaValidator.cppC 封装实现src/shared_modules/schema_validator/src/schemaValidator_c.cppschema 嵌入构建src/shared_modules/schema_validator/CMakeLists.txt单元测试src/shared_modules/schema_validator/tests/schemaValidator_test.cpp并发测试src/shared_modules/schema_validator/tests/schemaValidatorConcurrency_test.cpp【免费下载链接】wazuhWazuh - The Open Source Security Platform. Unified XDR and SIEM protection for endpoints and cloud workloads.项目地址: https://gitcode.com/GitHub_Trending/wa/wazuh创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考