Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(terraform): fix variable rendering for foreach resources with dot included names #5701

Merged
merged 1 commit into from
Oct 30, 2023
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
18 changes: 13 additions & 5 deletions checkov/terraform/graph_builder/local_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
from checkov.terraform.graph_builder.utils import (
get_attribute_is_leaf,
get_referenced_vertices_in_value,
attribute_has_nested_attributes, remove_index_pattern_from_str,
attribute_has_nested_attributes,
remove_index_pattern_from_str,
join_double_quote_surrounded_dot_split,
)
from checkov.terraform.graph_builder.utils import is_local_path
from checkov.terraform.graph_builder.variable_rendering.renderer import TerraformVariableRenderer
Expand Down Expand Up @@ -609,9 +611,11 @@ def update_dictionary_attribute(


def update_dictionary_attribute(
config: Union[List[Any], Dict[str, Any]], key_to_update: str, new_value: Any, dynamic_blocks: bool = False
config: Union[List[Any], Dict[str, Any]], key_to_update: str, new_value: Any, dynamic_blocks: bool = False
) -> Union[List[Any], Dict[str, Any]]:
key_parts = key_to_update.split(".")
if '"' in key_to_update:
key_parts = join_double_quote_surrounded_dot_split(str_parts=key_parts)

if isinstance(config, dict) and isinstance(key_parts, list):
key = key_parts[0]
Expand All @@ -624,17 +628,21 @@ def update_dictionary_attribute(
config[key] = to_list(new_value) if dynamic_blocks else new_value
return config
else:
config[key] = update_dictionary_attribute(inner_config, ".".join(key_parts[1:]), new_value, dynamic_blocks=dynamic_blocks)
config[key] = update_dictionary_attribute(
inner_config, ".".join(key_parts[1:]), new_value, dynamic_blocks=dynamic_blocks
)
else:
for key in config:
config[key] = update_dictionary_attribute(config[key], key_to_update, new_value, dynamic_blocks=dynamic_blocks)
config[key] = update_dictionary_attribute(
config[key], key_to_update, new_value, dynamic_blocks=dynamic_blocks
)
if isinstance(config, list):
return update_list_attribute(
config=config,
key_parts=key_parts,
key_to_update=key_to_update,
new_value=new_value,
dynamic_blocks=dynamic_blocks
dynamic_blocks=dynamic_blocks,
)
return config

Expand Down
31 changes: 31 additions & 0 deletions checkov/terraform/graph_builder/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,3 +394,34 @@ def get_attribute_is_leaf(vertex: TerraformBlock) -> Dict[str, bool]:
if other in attribute_is_leaf:
attribute_is_leaf[other] = False
return attribute_is_leaf


def join_double_quote_surrounded_dot_split(str_parts: list[str]) -> list[str]:
"""Joins back split strings which enclosed a dot by double quotes

ex.

['google_project_iam_binding', 'role["roles/logging', 'admin"]'] -> ['google_project_iam_binding', 'role["roles/logging.admin"]']

If someone finds a better solution feel free to replace it!
"""

new_str_parts = []
joined_str_parts: list[str] = []
for part in str_parts:
if not joined_str_parts:
if '"' not in part:
new_str_parts.append(part)
elif part.count('"') >= 2:
new_str_parts.append(part)
else:
joined_str_parts.append(part)
continue

joined_str_parts.append(part)

if '"' in part:
new_str_parts.append(".".join(joined_str_parts))
joined_str_parts = []

return new_str_parts
41 changes: 41 additions & 0 deletions tests/terraform/graph/graph_builder/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import pytest

from checkov.terraform.graph_builder.utils import join_double_quote_surrounded_dot_split


@pytest.mark.parametrize(
"input_parts,expected_parts",
[
(
["google_project_iam_binding", 'role["roles/logging', 'admin"]'],
["google_project_iam_binding", 'role["roles/logging.admin"]'],
),
(
["module", "google_project_iam_binding", 'role["roles/logging', 'admin"]'],
["module", "google_project_iam_binding", 'role["roles/logging.admin"]'],
),
(
[
"module",
"google_project_iam_binding",
'role["roles/logging',
'admin"]',
"module",
"google_project_iam_binding",
'role["roles/logging',
'admin"]',
],
[
"module",
"google_project_iam_binding",
'role["roles/logging.admin"]',
"module",
"google_project_iam_binding",
'role["roles/logging.admin"]',
],
),
],
ids=["resource", "module_resource", "complex"],
)
def test_join_double_quote_surrounded_dot_split(input_parts, expected_parts):
assert join_double_quote_surrounded_dot_split(str_parts=input_parts) == expected_parts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
variable "project_id" {
type = string
}

locals {
roles = [
"roles/run.developer",
]
}

resource "google_project_iam_binding" "role" {
for_each = toset(local.roles)
project = var.project_id
role = each.key

members = [
"user:[email protected]"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
project_id = "avengers"
23 changes: 23 additions & 0 deletions tests/terraform/graph/variable_rendering/test_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,3 +485,26 @@ def test_multiple_dynamic_blocks_value_not_supporting(self):
'dynamic'][1]['authorized_networks']['content'][0]['value']
# TODO - for now we don't support multiple dynamic blocks - the value_block_1 and value_block_2 needs to be diffrent and not overide each other
assert not value_block_1 != value_block_2


def test_foreach_with_tfvars(self):
# given
resources_dir = Path(TEST_DIRNAME) / "resources/foreach_examples/foreach_tfvars"
graph_manager = TerraformGraphManager("m", ["m"])

# when
local_graph, _ = graph_manager.build_graph_from_source_directory(str(resources_dir), render_variables=True)

# then
resource = local_graph.vertices[local_graph.vertices_by_block_type["resource"][0]]
self.assertDictEqual(
resource.config["google_project_iam_binding"]['role["roles/run.developer"]'],
{
"__address__": 'google_project_iam_binding.role["roles/run.developer"]',
"__end_line__": 19,
"__start_line__": 11,
"members": [["user:[email protected]"]],
"project": ["avengers"], # this is important it is correctly rendered
"role": ["roles/run.developer"],
},
)