Skip to content

Commit

Permalink
feat(terraform_plan): Support after_unknown evaluation of complex att…
Browse files Browse the repository at this point in the history
…ributes (#6784)

* Added function to handle complex case of after_unknwon in terraform plan

* Added test
  • Loading branch information
bo156 authored Oct 20, 2024
1 parent cbb7316 commit 20e9faa
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 0 deletions.
25 changes: 25 additions & 0 deletions checkov/terraform/plan_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,31 @@ def _eval_after_unknown(changes: dict[str, Any], resource_conf: dict[str, Any])
# In these cases, policies checking the existence of a value will succeed,
# but policies checking for concrete values will fail
resource_conf[k] = _clean_simple_type_list([TRUE_AFTER_UNKNOWN])
elif isinstance(v, list) and len(v) == 1 and isinstance(v[0], dict):
_handle_complex_after_unknown(k, resource_conf, v)


def _handle_complex_after_unknown(k: str, resource_conf: dict[str, Any], v: Any) -> None:
"""
Handles a case of an inner key generated with "after_unknown" value.
Example:
`
after_unknown: {
"logging_config": [
{
"bucket": true
}
],
}
`
"""
inner_keys = list(v[0].keys())
for inner_key in inner_keys:
if inner_key in (START_LINE, END_LINE):
# skip inner checkov keys
continue
if inner_key not in resource_conf[k]:
resource_conf[k][0][inner_key] = _clean_simple_type_list([TRUE_AFTER_UNKNOWN])


def _find_child_modules(
Expand Down
49 changes: 49 additions & 0 deletions tests/terraform/parser/resources/plan_after_unknown/tfplan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"format_version": "1.0",
"terraform_version": "1.1.3",
"planned_values": {
"root_module": {
"resources": [
{
"address": "aws_cloudfront_distribution.cloudfront",
"mode": "managed",
"type": "aws_cloudfront_distribution",
"name": "cloudfront",
"provider_name": "registry.terraform.io/hashicorp/aws",
"schema_version": 1,
"values": {
"logging_config": [
{
"include_cookies": false,
"prefix": "cloudfront"
}
]
}
}
]
}
},
"resource_changes": [
{
"address": "aws_cloudfront_distribution.cloudfront",
"mode": "managed",
"type": "aws_cloudfront_distribution",
"name": "cloudfront",
"provider_name": "registry.terraform.io/hashicorp/aws",
"change": {
"actions": [
"create"
],
"before": null,
"after": null,
"after_unknown": {
"logging_config": [
{
"bucket": true
}
]
}
}
}
]
}
12 changes: 12 additions & 0 deletions tests/terraform/parser/test_plan_parser.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import os
import unittest
from pathlib import Path
from unittest import mock

from pytest_mock import MockerFixture

from checkov.common.util.consts import TRUE_AFTER_UNKNOWN
from checkov.terraform.plan_parser import parse_tf_plan
from checkov.common.parsers.node import StrNode

Expand Down Expand Up @@ -80,6 +82,16 @@ def test_module_with_connected_resources(self):
resource_attributes = next(iter(resource_definition.values()))
self.assertTrue(resource_attributes['references_'])

@mock.patch.dict(os.environ, {"EVAL_TF_PLAN_AFTER_UNKNOWN": "True"})
def test_after_unknown_handling(self):
current_dir = os.path.dirname(os.path.realpath(__file__))
valid_plan_path = current_dir + "/resources/plan_after_unknown/tfplan.json"
tf_definition, _ = parse_tf_plan(valid_plan_path, {})
file_resource_definition = tf_definition['resource'][0]
resource_definition = next(iter(file_resource_definition.values()))
resource_attributes = next(iter(resource_definition.values()))
self.assertEqual(resource_attributes['logging_config'][0]["bucket"], [TRUE_AFTER_UNKNOWN])

def test_large_file(mocker: MockerFixture):
# given
test_file = Path(__file__).parent / "resources/plan_encodings/tfplan_mac_utf8.json"
Expand Down

0 comments on commit 20e9faa

Please sign in to comment.