Wazuh Engine defs 模块深度解析:JSON 定义变量与 `$variable` 替换系统的实现原理
发布时间:2026/9/14 10:23:22 作者:尧图编辑部 阅读量:1,286

Wazuh Engine defs 模块深度解析JSON 定义变量与$variable替换系统的实现原理【免费下载链接】wazuhWazuh - The Open Source Security Platform. Unified XDR and SIEM protection for endpoints and cloud workloads.项目地址: https://gitcode.com/GitHub_Trending/wa/wazuhWazuh 引擎engine中的defs模块提供了一套变量定义与替换系统策略policy与资产asset可以在 JSON 中声明可复用的常量、路径、URL 等命名值再由构建器在编译期统一解析。阅读本文后你将完整掌握$variable语法的替换规则、定义间依赖的 DFS 预解析算法含循环引用检测、IDefinitions/IDefinitionsBuilder接口契约以及该模块如何被builder模块以依赖注入方式消费。以下内容基于 src/engine/source/defs/README.md 整理并结合 defs.cpp、defs.hpp 等源码逐节印证。一、核心概念Definition、变量引用与依赖解析1.1 Definition定义一个 Definition 就是 JSON 对象中的一个具名值JSON 对象的每个键是定义名值可以是任意合法 JSON 类型字符串、数字、布尔、null、数组、对象。示例{ protocol: https, host: api.example.com, port: 8080, base_url: $protocol://$host:$port }1.2 变量引用Variable Reference字符串中使用$name语法引用变量。当replace()被调用时所有$name模式都会被替换为对应的定义值$varname— 替换为varname的值\$varname— 转义输出字面量$varname$nonexistent— 若定义不存在则原样保留。1.3 依赖解析Dependency Resolution定义可以引用其他定义。依赖在构造时通过 DFS深度优先搜索算法解析递归遍历定义之间的依赖关系先解析叶子定义再自底向上回传结果将解析后的值缓存起来避免重复计算检测循环引用一旦发现即抛出错误。{ base: /api, version: v1, endpoint: $base/$version }解析完成后$endpoint展开为/api/v1。源码印证preResolveDefinitions()在构造时把每个定义的字符串值缓存进m_resolvedDefinitions见 defs.cppresolveDefinitionDFS()负责递归展开依赖见 defs.cpp。这意味着后续每次replace()调用都只做纯字符串替换无需再递归查找——这正是构造时一次换运行时多次的设计取舍。二、架构三层职责划分README 给出的架构如下消费者Builder 模块从 store 读取定义 JSON经IDefinitionsBuilder工厂构建IDefinitions对象再在资产配置中解析$variable┌──────────────────────────────────────────────────────────────┐ │ Consumer (Builder module) │ │ │ │ 1. Reads definition JSON from the store │ │ 2. Builds a Definitions object via IDefinitionsBuilder │ │ 3. Uses IDefinitions to resolve $variables in asset configs │ └────────────────────────┬─────────────────────────────────────┘ │ json::Json ▼ ┌──────────────────────────────────────────────────────────────┐ │ defs::IDefinitionsBuilder (Factory) │ │ Creates IDefinitions from a JSON object │ └────────────────────────┬─────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ defs::Definitions │ │ │ │ ┌─────────────────┐ ┌──────────────────────────────────┐ │ │ │ m_definitions │ │ m_resolvedDefinitions │ │ │ │ (raw JSON) │ │ (pre-resolved string cache) │ │ │ └─────────────────┘ └──────────────────────────────────┘ │ │ │ │ • get(name) → returns raw JSON value │ │ • contains(name) → checks existence │ │ • replace(input) → substitutes $variables in a string │ └──────────────────────────────────────────────────────────────┘Definitions内部维护两份数据定义见 defs.hppm_definitions原始 JSONstd::unique_ptrjson::Json供get()/contains()做点路径导航m_resolvedDefinitionsstd::unordered_mapstd::string, std::string构造时预解析好的字符串缓存供replace()高速查表。目录结构defs/ ├── CMakeLists.txt # 构建目标defs、defs::idefinitions、defs::mocks ├── interface/ │ └── defs/ │ └── idefinitions.hpp # 公开接口IDefinitions、IDefinitionsBuilder ├── include/ │ └── defs/ │ └── defs.hpp # 具体实现Definitions、DefinitionsBuilder ├── src/ │ └── defs.cpp # Definitions 实现 └── test/ ├── mocks/ │ └── defs/ │ ├── mockDefinitions.hpp # IDefinitions 与 IDefinitionsBuilder 的 GMock mock │ ├── singleDef.hpp # 测试辅助固定取值的单一定义 │ └── failDef.hpp # 测试辅助总是失败的 IDefinitions └── src/ └── unit/ └── defs_test.cpp # 单元测试三、公开接口IDefinitions 与 IDefinitionsBuilder接口定义在 interface/defs/idefinitions.hpp与实现完全解耦——这是该模块最重要的接口隔离设计。3.1IDefinitions访问与使用定义的主契约class IDefinitions { // 按点路径名如 /key 或 /nested/key获取定义的 JSON 值 virtual json::Json get(std::string_view name) const 0; // 检查定义在给定点路径上是否存在 virtual bool contains(std::string_view name) const 0; // 将输入字符串中所有 $variable 引用替换为解析后的值 virtual std::string replace(std::string_view input) const 0; };接口注释明确了get()的异常契约定义不存在时抛出std::runtime_error。实现defs.cpp通过m_definitions-getJson(name)做点路径导航未命中即抛Definition {} not found。3.2IDefinitionsBuilder从 JSON 对象创建IDefinitions实例的工厂class IDefinitionsBuilder { virtual std::shared_ptrIDefinitions build(const json::Json value) const 0; };具体实现DefinitionsBuilder仅一行核心逻辑defs.hppstd::shared_ptrIDefinitions build(const json::Json value) const override { return std::make_sharedDefinitions(value); }工厂模式的价值在于下游builder模块只依赖接口指针可以在测试中注入 mock 或失败实例test/mocks/defs/下提供了MockDefinitions、MockDefinitionsBuilder、SingleDef、FailDef等测试替身见 mockDefinitions.hpp。四、实现细节源码级4.1 构造Definitions(const json::Json)构造流程defs.cpp共四步校验输入必须是 JSON 对象否则抛出Definitions must be an object, got {type}拒绝任何以$开头的键名$前缀为变量引用保留抛出Definition name {} cannot start with $保存原始定义到m_definitions注释说明这是一份紧凑拷贝规模通常较小调用preResolveDefinitions()计算m_resolvedDefinitions。单元测试用参数化用例覆盖了这些边界defs_test.cppnull、数组、字符串字面量均判为非法空对象{}、数字/布尔/null/数组/对象值均合法{$a: 1}与{$invalid: test}判为非法而valid_123、_underscore、CamelCase均合法。4.2 预解析preResolveDefinitions与resolveDefinitionDFS构造时所有字符串值中含$引用的定义都会被解析成最终形态。这是一次性的开销换来后续replace()的快速调用。resolveDefinitionDFS()defs.cpp的关键行为维护visited 集合与递归栈inStack用于环检测。若待解析的名字已在inStack中抛出Circular reference detected in definition {}引用不存在的定义不会报错rawDefs.find(defName)未命中时直接返回$ defName源码注释说明这在 check/parse 阶段是正常情形——引用可能留给下游处理扫描值中的$时跳过被反斜杠转义的$resolved[pos-1] \\提取的变量名仅由字母、数字、下划线组成解析完成后从inStack移除并把结果写入m_resolvedDefinitions缓存。4.3 变量替换replace的三重保障replaceVariables()defs.cpp与replaceVariableInString()defs.cpp共同实现替换有三道防线最长名优先排序变量名按长度降序排列后再逐个替换防止短名字破坏长名字——例如若先替换$ab它可能误伤$abc的前缀部分边界检查$variable命中后必须检查其后一个字符不是字母、数字或下划线否则视为更长标识符的一部分并跳过defs.cpp转义处理$前一个字符是\时删除反斜杠并保留字面量$var跳过本次替换defs.cpp。4.4get与contains点路径导航两者都作用于原始m_definitionsJSON/key— 顶层键/nested/key— 嵌套对象访问/array/0— 数组下标访问。单元测试中的真实用例defs_test.cpp{nested: {key: value}}下取/nested/key得value{array: [1,2,3]}下取/array/0得1甚至支持深层组合{complex: {array: [{id: 123}]}}取/complex/array/0/id得123。五、变量替换规则速查表输入定义输出规则$host{host: localhost}localhost基本替换$host:$port{host: localhost, port: 8080}localhost:8080多变量\$host{host: localhost}$host转义——输出字面量$missing{host: localhost}$missing未定义——原样保留$url{host: h, url: http://$host}http://h依赖链已解析$ab{ab: X, abc: Y}X最长优先匹配避免前缀冲突$abc{ab: X, abc: Y}Y边界检查保证正确匹配六、在 Builder 模块中的真实用法builder模块是defs的首要消费者。典型流程// 1. 引擎启动时创建 DefinitionsBuilder auto defsBuilder std::make_shareddefs::DefinitionsBuilder(); // 2. Builder 以依赖注入方式接收它 Builder builder(..., defsBuilder, ...); // 3. 构建策略时builder 从 store 读取定义 JSON // 并调用 defsBuilder-build(definitionsJson) 创建 IDefinitions 实例 // 4. 构建资产时check/parse 表达式中的 $variable 被解析 std::string resolved definitions.replace(field $expected_value);源码印证了注入链路builder::policy::AssetBuilder构造函数接收std::shared_ptrdefs::IDefinitionsBuilderassetBuilder.hppPolicy与Builder顶层同样持有该依赖builder.hpp、builder.cpp。注意这里注入的都是接口类型而非具体类因此策略测试可以用 mock 替换整个定义子系统。七、CMake 构建目标CMakeLists.txt 定义四个目标目标别名类型说明defs_idefinitionsdefs::idefinitionsINTERFACE仅接口IDefinitions、IDefinitionsBuilder依赖basedefs—STATIC具体实现依赖defs::idefinitions与basedefs_mocksdefs::mocksINTERFACE供外部测试使用的 GMock mock依赖defs::idefinitionsdefs_utest—EXECUTABLE单元测试gtest_discover_tests注册defs_mocks与defs_utest均在ENGINE_BUILD_TEST开关下才构建mock 目标只暴露interface头目录保证外部模块引用 mock 时不会漏进具体实现头文件。八、测试体系与循环引用检测单元测试位于 defs_test.cpp447 行采用参数化测试组织覆盖范围构造合法/非法 JSON 输入、$前缀键名拒绝Get基本键、嵌套键、数组下标、缺失键抛错Contains扁平、嵌套、数组路径的存在性检查Replace基本替换、多变量、转义变量、嵌套定义、复杂依赖链、前缀冲突、特殊字符、边界情形字符串末尾的$、$后跟非字母字符循环引用直接环a→b→a、传递环a→b→c→a、自引用a→a错误信息验证异常消息包含有用的诊断信息性能可处理 1000 定义特殊 JSON 值数字、布尔、null、Unicode、空字符串Builder验证DefinitionsBuilder能创建合法实例并传播错误。循环引用检测的参数化用例defs_test.cpp尤其值得参考覆盖了多种形态// 直接环 {a: $b, b: $a} // 抛 std::runtime_error // 传递环 {a: $b, b: $c, c: $a} // 抛 std::runtime_error // 自引用 {a: $a} // 抛 std::runtime_error // 环嵌在字符串中间 {a: prefix $b suffix, b: start $a end} // 抛 std::runtime_error // 多变量混合环 {a: $b$c, b: $c$a, c: value} // 抛 std::runtime_error运行方式在 engine 构建目录下./defs_utest九、关键设计决策回顾构造时预解析定义间依赖在构造时一次性解析完毕replace()变成无递归的纯字符串替换代价是构造稍贵。最长优先替换变量按名字长度降序替换使$prefix不会错误地匹配进$prefix_extended无需额外分隔符即可解决前缀冲突。边界感知匹配仅当变量名后的字符不是字母、数字或下划线时匹配才成立防止在更长标识符内发生部分匹配。转义机制\$var产出字面量$var允许用户书写不应被视为变量引用的美元符号模式。环检测快速失败循环引用在构造期即被检测并以清晰错误消息抛出避免解析期死循环。接口隔离IDefinitions与IDefinitionsBuilder同实现分离使builder模块只依赖接口、测试可轻松打桩。参考文件模块文档src/engine/source/defs/README.md接口定义src/engine/source/defs/interface/defs/idefinitions.hpp类声明src/engine/source/defs/include/defs/defs.hpp核心实现src/engine/source/defs/src/defs.cpp构建脚本src/engine/source/defs/CMakeLists.txt单元测试src/engine/source/defs/test/src/unit/defs_test.cppMock 与测试替身src/engine/source/defs/test/mocks/defs/mockDefinitions.hpp消费方src/engine/source/builder/src/policy/assetBuilder.hpp【免费下载链接】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),仅供参考