Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions checkov/terraform/plan_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,24 @@ def _handle_complex_after_unknown(k: str, resource_conf: dict[str, Any], v: Any)
if inner_key in (START_LINE, END_LINE):
# skip inner checkov keys
continue
if inner_key not in resource_conf[k]:
if isinstance(resource_conf[k][0], dict):
resource_conf[k][0][inner_key] = _clean_simple_type_list([TRUE_AFTER_UNKNOWN])
elif isinstance(resource_conf[k][0], list) and isinstance(resource_conf[k][0][0], dict):
resource_conf[k][0][0][inner_key] = _clean_simple_type_list([TRUE_AFTER_UNKNOWN])
resource_conf_value = resource_conf[k]
if inner_key not in resource_conf_value and isinstance(resource_conf_value, list):
for i in range(len(resource_conf_value)):
if isinstance(resource_conf_value[i], dict):
if _validate_after_unknown_list_not_empty(inner_key, k, resource_conf_value[i]):
resource_conf_value[i][inner_key] = _clean_simple_type_list([TRUE_AFTER_UNKNOWN])
elif isinstance(resource_conf_value[i], list) and isinstance(resource_conf_value[i][0], dict):
if _validate_after_unknown_list_not_empty(inner_key, k, resource_conf_value[i][0]):
resource_conf_value[i][0][inner_key] = _clean_simple_type_list([TRUE_AFTER_UNKNOWN])


def _validate_after_unknown_list_not_empty(inner_key: str, k: str, value: dict[str, Any]) -> bool:
"""
If the inner key is a list - we want to check it's not empty, if not we handle it in the original way.
"""
return ((inner_key not in value) or
(inner_key in value and not isinstance(value[inner_key], list)) or
(isinstance(value[inner_key], list) and value[inner_key] != [] and value[inner_key][0] != []))


def _find_child_modules(
Expand Down
33 changes: 32 additions & 1 deletion tests/terraform/parser/test_plan_parser.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import copy
import os
import unittest
from pathlib import Path
from typing import Any
from unittest import mock

import pytest
from pytest_mock import MockerFixture

from checkov.common.util.consts import TRUE_AFTER_UNKNOWN
from checkov.terraform.plan_parser import parse_tf_plan, _sanitize_count_from_name, _handle_complex_after_unknown
from checkov.terraform.plan_parser import parse_tf_plan, _sanitize_count_from_name, _handle_complex_after_unknown, \
_validate_after_unknown_list_not_empty
from checkov.common.parsers.node import StrNode


Expand Down Expand Up @@ -146,6 +150,33 @@ def test_handle_complex_after_unknown(self):
_handle_complex_after_unknown(key, resource, value)
assert resource == {'tags': [[{'custom_tags': ['true_after_unknown']}]]}

def test_handle_complex_after_unknown_with_empty_list(self):
resource = {"network_configuration": [
{
"endpoint_configuration": [
]
}
]}
key: str = 'network_configuration'
value = [{"endpoint_configuration": []}]
_handle_complex_after_unknown(key, resource, value)
assert resource == {'network_configuration': [{"endpoint_configuration": []}]}

@pytest.mark.parametrize("inner_key, k, is_inner_list", [
("endpoint_configuration", "network_configuration", False),
("endpoint_configuration", "network_configuration", True)
])
def test_handle_complex_after_unknown(inner_key: str, k: str, is_inner_list: bool) -> None:
if is_inner_list:
# We cannot parametrize a dict object, so we use a boolean to decide which conf to use
resource_conf = {'network_configuration': [[{"endpoint_configuration": []}]]}
else:
resource_conf = {'network_configuration': [{"endpoint_configuration": []}]}
value = [{"endpoint_configuration": []}]
resource_conf_copy = copy.deepcopy(resource_conf)
_handle_complex_after_unknown(k, resource_conf, value)
assert resource_conf == resource_conf_copy


def test_large_file(mocker: MockerFixture):
# given
Expand Down
Loading