
Checkov Terraform 解析场景测试体系expected.json 与 eval.json 协议详解【免费下载链接】checkovPrevent cloud misconfigurations and find vulnerabilities during build-time in infrastructure as code, container images and open source packages with Checkov by Bridgecrew.项目地址: https://gitcode.com/GitHub_Trending/ch/checkov本文深入解析 Checkov 项目中 Terraform 解析器的场景化测试体系该体系以tests/terraform/parser/resources/parser_scenarios目录为核心通过场景目录 期望输出文件的方式系统性验证 HCL2 解析与变量求值evaluation的正确性。读完本文你将理解expected.json与eval.json两个核心协议文件的结构与语义、相对路径到绝对路径的自动转换机制以及如何借助 40 个覆盖真实 Terraform 语法边界的场景用例来保障解析器质量并掌握为解析器新增测试场景的完整方法。一、场景测试体系总览Checkov 的 Terraform 解析器位于 checkov/terraform/tf_parser.py负责将 Terraform 配置文件解析为结构化的定义数据definitions并进一步交给图构建与变量渲染模块。为了验证解析结果符合预期项目在 tests/terraform/parser/resources/parser_scenarios 目录下维护了一套场景目录 期望文件的测试基线。该目录下的每个子目录代表一个独立解析场景其命名直接反映了被测的语法或解析行为例如simple_bucket_single_file最简单的单文件、单资源解析module_simple、module_multiple_usage、module_reference、module_output_reference模块解析与模块间引用module_matryoshka_nested_module_enable、nested_modules_instances_enable嵌套模块tfvars、tfvars_outside_dir变量文件.tfvars加载与优先级local_block、compound_local、local_bool_string_conversionlocals块求值merge_function、concat_function、map_function、tobool_function、tolist_function、tomap_function、tonumber_function、toset_function、tostring_functionHCL2 内置函数求值ternaries、ternary_793三元表达式count_evalcount与多模块组合的真实生产案例源自 Google Cloud 示例account_dirs_and_modules多账号目录结构与模块混排bad_ref_fallbacks、bogus_function、empty_file、colon异常输入与边界行为每个场景目录内至少包含一个expected.json文件当同时需要验证变量求值evaluation结果时还会附带一个eval.json文件。部分场景还配有说明性 README例如 doc_evaluations_verify/README.md 明确写道This is verifying behavior of the Concepts/Evaluations.md doc.——该场景专门用于验证官方文档中描述的评估行为与代码实现是否一致。二、expected.json解析输出的黄金基线场景目录入口处的 README.md 对该体系的核心约定做了精炼说明Child directories contain parsing scenarios along with anexpected.jsonfile with the total expected result output. During real evaluation all files will use absolute paths. To make tests work across various systems, test expectations are written with relative paths and tests will convert to absolute paths on the fly.这短短几句话概括了三个关键设计决策场景目录存放待解析的 Terraform 源文件*.tf、*.tf.json等expected.json保存解析后的完整期望输出total expected result output路径可移植性约定真实解析时所有文件使用绝对路径但期望文件一律写成相对路径由测试代码在运行时动态转换为绝对路径从而保证测试在不同系统、不同检出目录下都能通过。2.1 最小场景剖析simple_bucket_single_file以最简单的场景为例源文件 simple_bucket_single_file/main.tf 内容为resource aws_s3_bucket test { bucket my-test-bucket }对应的 simple_bucket_single_file/expected.json 为{ {\file_path\: \main.tf\, \tf_source_modules\: null}: { resource: [ { aws_s3_bucket: { test: { bucket: [ my-test-bucket ], __start_line__: 1, __end_line__: 3, __address__: aws_s3_bucket.test } } } ] } }从中可以提炼出expected.json的三层结构协议顶层键TFDefinitionKey每个被解析文件的标识以 JSON 序列化字符串表示包含file_path相对路径与tf_source_modules来源模块链根文件为null。这与源码中 checkov/terraform/modules/module_objects.py 定义的TFDefinitionKey数据结构一一对应。中间层块类型分组按块类型组织如resource、module、variable、locals等。叶子层块详情块名、属性值统一包装为数组、以及三个解析器注入的元数据字段__start_line__/__end_line__块在源文件中的起止行号__address__Checkov 中用于定位该块的逻辑地址如aws_s3_bucket.test、module.bucket.aws_s3_bucket.mybucket。2.2 模块场景resolved与来源模块链当涉及模块调用时期望文件的结构会显著复杂化。module_simple/expected.json 展示了模块解析的输出形态{ {\file_path\: \main.tf\, \tf_source_modules\: null}: { module: [ { bucket: { source: [./bucket], __resolved__: [{\file_path\: \bucket/bucket.tf\, \tf_source_modules\: {\path\: \main.tf\, \name\: \bucket\, \foreach_idx\: null, \nested_tf_module\: null}}], __start_line__: 1, __end_line__: 3, __address__: bucket } } ] }, {\file_path\: \bucket/bucket.tf\, \tf_source_modules\: {\path\: \main.tf\, \name\: \bucket\, \foreach_idx\: null, \nested_tf_module\: null}}: { resource: [ { aws_s3_bucket: { mybucket: { bucket: [MyBucket], __start_line__: 1, __end_line__: 3, __address__: module.bucket.aws_s3_bucket.mybucket } } } ] } }该场景由 module_simple/main.tfmodule bucket { source ./bucket }与 module_simple/bucket/bucket.tf一个 S3 Bucket 资源构成。解析结果中module.bucket块通过__resolved__字段指向被解析到的模块文件携带完整的TFModule信息path、name、foreach_idx、nested_tf_module供后续图构建阶段追踪模块依赖模块内部资源出现在第二个顶层键中其__address__被改写为module.bucket.aws_s3_bucket.mybucket体现了模块作用域的地址语义运行时测试代码会对__resolved__中序列化的TFDefinitionKey同样执行相对→绝对路径替换见load_expected_data中的replace_tf_definition_obj_keys逻辑保证模块引用指向真实存在的文件。在 checkov/terraform/graph_builder/utils.py 中__resolved__字段会被消费——图构建时通过它把模块实例与对应定义文件关联起来这正是解析结果与后续策略扫描之间的桥梁。三、eval.json变量求值的独立断言通道除了解析结构本身Checkov 还会对 Terraform 中的变量variable求值过程进行验证。当场景需要同时验证求值结果时会额外提供eval.json文件其结构与expected.json完全不同专门描述哪些变量被求值、值来自哪里、以及作用在哪个属性上。以 variable_defaults/eval.json 为例{ name_doesnt_matter.tf: { BUCKET_NAME: { var_file: name_doesnt_matter.tf, value: this-is-my-default, definitions: [ { definition_name: BUCKET_NAME, definition_expression: ${var.BUCKET_NAME}, definition_path: resource/0/aws_s3_bucket/test/bucket/0 } ] } } }其语义如下顶层键求值发生所在的文件第二层键被求值的变量名var_file变量实际取值来源的文件此处因变量使用默认值来源即声明文件本身value求值后的最终值definitions该变量被引用到的全部位置其中definition_path使用资源类型/索引/块名/属性名/索引的路径格式定位到具体属性如resource/0/aws_s3_bucket/test/bucket/0definition_expression记录原始引用表达式如${var.BUCKET_NAME}。类似的场景还有 local_block/eval.json其源文件 local_block/name_doesnt_matter.tf 中locals与资源引用local.BUCKET_NAME配合验证 locals 求值、local_bool_string_conversion/eval.json、variable_defaults_separate_files/eval.json变量默认值分散在独立variables.tf中的场景。在 Checkov 运行时这些求值上下文会被填充到base_runner.py中的evaluations_contextcheckov/terraform/base_runner.py并最终体现在扫描结果的 Evaluations 部分供用户追溯每个策略判定所依赖的变量值来源。四、测试驱动机制从相对路径到绝对路径场景测试的主入口位于 tests/terraform/graph/variable_rendering/test_render_scenario.py其中每个test_*方法对应一个场景目录最终汇聚到统一的go(dir_name, ...)驱动方法。其核心流程为定位场景目录os.path.realpath(os.path.join(TEST_DIRNAME, ../../parser/resources/parser_scenarios, dir_name))即从测试文件所在目录回溯到场景根目录构建图实例化TerraformGraphManager调用build_graph_from_source_directory(resources_dir, render_variablesTrue, vars_filesvars_files)执行解析、变量渲染与图构建还原定义通过convert_graph_vertices_to_tf_definitions将图顶点转换回与expected.json同构的 tf definitions 结构加载期望load_expected(...)读取场景目录下的expected.json路径归一化这是最关键的一步对应load_expected_data函数见 test_render_scenario.py——遍历期望数据的每一个键将相对文件名拼接为绝对路径同时对TFDefinitionKey中内嵌的file_path、模块引用路径以及__resolved__列表做同样的替换使期望数据与实际解析输出的绝对路径对齐逐块断言match_blocks/match_resources按块类型、块名逐一比对期望与实际的字段值任何不一致都会输出包含Expected / Actual的详细差异信息方便定位解析回归。此外load_expected支持两种变体different_expected参数允许针对同一场景在特定条件下覆盖个别字段的期望值例如tfvars场景中通过调整--var-file顺序来验证变量优先级见下文replace_expectedTrue从resources目录读取以{dir_name}_expected.json命名的旧基线替换场景目录内的expected.json用于解析行为调整后的基线迁移。该驱动方法还通过mock.patch.dict(os.environ, {RENDER_VARIABLES_ASYNC: False, ...})固定了变量渲染的异步开关保证测试环境一致性。五、典型场景实战解读5.1 tfvars变量文件的加载与优先级tfvars 场景集中验证了 Checkov 对 Terraform 变量文件加载语义的实现。目录内文件包括main.tf声明 6 个variable块其中foo、list_data、map_data无默认值其余有默认值一个 S3 Bucket 资源将全部变量拼接进bucket属性terraform.tfvars为foo含非 ASCII 字符fü、list_data、map_data提供值x.auto.tfvars、y.auto.tfvars、other1.tfvars、other2.tfvars、other3.tfvars模拟自动加载与显式--var-file指定的文件。该测试在 test_render_scenario.py 中以注释明确记录了 Checkov 遵循的变量求值优先级variable块中的默认值terraform.tfvars*.auto.tfvars文件按字母序后者覆盖前者通过--var-file显式指定的文件。因此foo nimrodIsCöol来自other2.tfvars覆盖y.auto.tfvars、x.auto.tfvars、terraform.tfvars、list_data [nine, ten]来自y.auto.tfvars、only_here与other_var_1保持声明默认值、other_var_2被other2.tfvars覆盖为xyz。测试还通过交换vars_files[other2.tfvars, other3.tfvars]与[other3.tfvars, other2.tfvars]两次运行断言文件顺序os.scandir返回顺序对最终求值结果的影响符合预期——即--var-file列表中靠后的文件拥有更高优先级。5.2 locals 与函数求值merge 的多种组合compound_local/checkov.tf 验证了locals之间的级联引用AUTHORIZATION_CODE由四个 local 拼接而成而 merge_function/main.tf 则系统性地覆盖了merge()函数的各种调用形态local 与 local、local 与字面量 map、三参数混合合并嵌套 mergemerge(local.common_tags, merge({...}, {...}))单参数调用merge(local.common_tags)、merge({Tag4 four})多行书写、属性覆盖后合并的键覆盖先前的同名键如doc_example1中c最终为z资源属性内直接调用merge(..., {Name Bob-${local.static1}-${local.static2}})验证函数调用与字符串插值的组合求值。文件中的注释还记录了一个有趣的解析器边界当 map 值内包含引号{b\ , evil}时 HCL 解析器会产生怪异行为因此该用例被显式注释掉并附上解析异常说明——这正是场景化测试记录已知边界、防止行为漂移价值的直接体现。5.3 三元表达式与布尔求值ternaries/main.tf 验证了true ? correct : wrong与false ? wrong : correct两类三元表达式的求值结果同时以注释形式标注了若干未启用的用例及原因多行三元、字符串true三元TODO 指向test_hcl2_load_assumptions.py中的test_weird_ternary_string_clipping、比较表达式三元等。这些注释不是随意留下的而是精确记录了当前解析器能力边界防止他人误以为这些语法已被支持。5.4 count_eval真实生产形态的多模块组合count_eval 是体量最大的场景之一直接取自 Google Cloud 官方 Terraform 示例顶层 main.tf 依次调用vpc、subnets、routes三个模块其中subnets引用module.vpc.network_name的输出routes又通过module_depends_on [module.subnets.subnets]显式声明模块间依赖。目录下的modules/内包含 vpc、subnets、subnets-beta、routes、routes-beta、fabric-net-firewall、fabric-net-svpc-access、network-peering 等 8 个子模块完整覆盖了模块输出引用、模块依赖、变量透传等复杂解析路径是检验解析器在真实工程规模下稳定性的重要基线。5.5 异常与边界输入empty_file空文件不应导致解析崩溃bogus_function非法函数调用该用例当前被skipTest(invalid values are not supported)跳过说明非法值暂不支持bad_ref_fallbacks 与 bad_tf_nested_modules_enable坏引用与坏模块场景后者注释指出会触发TFParser内部的_clean_bad_definitions清理函数null_variables_651对应 GitHub issue 651当前实现下保留原始变量引用故该测试被跳过colon文件名含冒号等特殊字符的解析。这些场景共同说明该测试体系不仅覆盖正常功能还显式维护了一张已知不支持/已知行为差异清单任何解析器改动都可能因触碰到这些边界用例而在 CI 中暴露。六、与解析器源码的对应关系场景期望文件验证的正是 checkov/terraform/tf_parser.py 的输出。其核心入口parse_directorytf_parser.py的调用链为parse_directory └─ load_tf_modules(...) # 注册并加载外部/内部模块 └─ _parse_directory(dir_filter, vars_files) └─ _internal_dir_load(...) # 按目录扫描 .tf 文件并写入 out_definitions └─ handle_variables(...) # 处理 terraform.tfvars / *.auto.tfvars / --var-file └─ _update_resolved_modules() # 填充 module 块的 __resolved__ 字段其中handle_variables实现了上文 5.1 节描述的变量加载优先级_internal_dir_load通过os.scandir遍历目录并调用_load_files逐个解析文件_update_resolved_modules负责生成expected.json中见到的__resolved__模块引用链。单文件入口parse_filetf_parser.py则支持.tf、.tf.json、.hcl三种扩展名对应场景目录中的 json_807/cdk.tf.jsonCDK 生成的 JSON 格式 Terraform。此外tests/terraform/checks/resource/aws/test_CloudfrontDistributionLogging.py 与 tests/terraform/runner/test_runner.py 等测试也会直接引用parser_scenarios下的目录作为解析输入说明该资源目录同时被解析器测试、图构建测试、策略测试和 runner 集成测试共享是一套一处维护、多方复用的公共测试资产。七、如何新增一个解析场景若要在本地验证或扩展这套测试体系可以遵循以下流程创建场景目录在 tests/terraform/parser/resources/parser_scenarios 下新建一个以行为命名的目录例如my_scenario/放入源文件编写待解析的 Terraform 文件main.tf、variables.tf、模块子目录等覆盖你关心的语法或引用形态生成 expected.json运行解析器导出实际输出整理为期望基线注意文件路径必须使用相对路径与simple_bucket_single_file等现有场景保持一致的键格式可选添加 eval.json若需要断言变量求值则按variable_defaults/eval.json的格式补充求值期望数据包含var_file、value、definitions注册测试方法在 tests/terraform/graph/variable_rendering/test_render_scenario.py 中新增def test_my_scenario(self): self.go(my_scenario)运行验证在仓库根目录执行对应测试例如python -m pytest tests/terraform/graph/variable_rendering/test_render_scenario.py -k my_scenario确认通过若解析行为发生变更需要迁移基线可借助replace_expectedTrue机制在resources/下维护旧基线。结语parser_scenarios场景目录是 Checkov Terraform 解析与变量求值质量保障的基石expected.json锁定解析输出的结构契约eval.json锁定变量求值的语义契约相对路径约定保证了测试的可移植性而 40 个覆盖正常功能、模块引用、变量优先级、内置函数、三元表达式与异常输入的场景则共同勾勒出解析器能力的完整边界。理解这套协议无论是排查解析问题、贡献新场景还是深入 Checkov 解析器实现都能事半功倍。【免费下载链接】checkovPrevent cloud misconfigurations and find vulnerabilities during build-time in infrastructure as code, container images and open source packages with Checkov by Bridgecrew.项目地址: https://gitcode.com/GitHub_Trending/ch/checkov创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考