Skip to content

Commit

Permalink
Merge branch 'bridgecrewio:main' into fix-terraform-foreach-bool
Browse files Browse the repository at this point in the history
  • Loading branch information
alanszlosek authored Oct 25, 2024
2 parents 631e199 + 3800929 commit 3bb9afe
Show file tree
Hide file tree
Showing 64 changed files with 7,614 additions and 5,862 deletions.
66 changes: 65 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,70 @@
# CHANGELOG

## [Unreleased](https://github.com/bridgecrewio/checkov/compare/3.2.254...HEAD)
## [Unreleased](https://github.com/bridgecrewio/checkov/compare/3.2.269...HEAD)

## [3.2.269](https://github.com/bridgecrewio/checkov/compare/3.2.268...3.2.269) - 2024-10-23

### Bug Fix

- **terraform:** Fix crash when version isn't a float - [#6783](https://github.com/bridgecrewio/checkov/pull/6783)

## [3.2.268](https://github.com/bridgecrewio/checkov/compare/3.2.267...3.2.268) - 2024-10-20

### Feature

- **terraform_plan:** Support after_unknown evaluation of complex attributes - [#6784](https://github.com/bridgecrewio/checkov/pull/6784)

## [3.2.267](https://github.com/bridgecrewio/checkov/compare/3.2.266...3.2.267) - 2024-10-16

- no noteworthy changes

## [3.2.266](https://github.com/bridgecrewio/checkov/compare/3.2.262...3.2.266) - 2024-10-15

### Feature

- **arm:** unsupported module soft fail - [#6775](https://github.com/bridgecrewio/checkov/pull/6775)

## [3.2.262](https://github.com/bridgecrewio/checkov/compare/3.2.258...3.2.262) - 2024-10-14

### Feature

- **terraform:** 2 new checks - [#6764](https://github.com/bridgecrewio/checkov/pull/6764)
- **terraform:** Add s3 data transport check - [#6763](https://github.com/bridgecrewio/checkov/pull/6763)

### Bug Fix

- **helm:** Remove helm target dir after scanning - [#6767](https://github.com/bridgecrewio/checkov/pull/6767)
- **kubernetes:** Handle non-sting params in command - [#6768](https://github.com/bridgecrewio/checkov/pull/6768)

## [3.2.258](https://github.com/bridgecrewio/checkov/compare/3.2.257...3.2.258) - 2024-10-13

### Bug Fix

- **terraform:** Set timeout for parsing Terraform files with hcl2. - [#6759](https://github.com/bridgecrewio/checkov/pull/6759)

## [3.2.257](https://github.com/bridgecrewio/checkov/compare/3.2.256...3.2.257) - 2024-10-06

### Bug Fix

- **ansible:** handle empty tasks - [#6751](https://github.com/bridgecrewio/checkov/pull/6751)

## [3.2.256](https://github.com/bridgecrewio/checkov/compare/3.2.254...3.2.256) - 2024-10-01

### Feature

- **terraform:** New checks - [#6720](https://github.com/bridgecrewio/checkov/pull/6720)

### Bug Fix

- **general:** Fix operator docs - [#6735](https://github.com/bridgecrewio/checkov/pull/6735)
- **sca:** add Pipfile and Pipfile.lock to supported package files list - [#6746](https://github.com/bridgecrewio/checkov/pull/6746)
- **terraform:** extend CKV2_AWS_5 to include DMS Serverless (#6628) - [#6630](https://github.com/bridgecrewio/checkov/pull/6630)
- **terraform:** Remove dataproc.admin from multiple checks - [#6725](https://github.com/bridgecrewio/checkov/pull/6725)
- **terraform:** Security group attached to an Elastic DocumentDB cluster is not recognized by check CKV2_AWS_5 - [#6687](https://github.com/bridgecrewio/checkov/pull/6687)

### Documentation

- **general:** update README.md - [#6719](https://github.com/bridgecrewio/checkov/pull/6719)

## [3.2.254](https://github.com/bridgecrewio/checkov/compare/3.2.253...3.2.254) - 2024-09-15

Expand Down
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ types-colorama = "<0.5.0,>=0.4.3"
# REMINDER: Update "install_requires" deps on setup.py when changing
#
bc-python-hcl2 = "==0.4.2"
bc-detect-secrets = "==1.5.15"
bc-detect-secrets = "==1.5.17"
bc-jsonpath-ng = "==1.6.1"
pycep-parser = "==0.4.1"
tabulate = ">=0.9.0,<0.10.0"
Expand Down
1,870 changes: 1,025 additions & 845 deletions Pipfile.lock

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions checkov/ansible/graph_builder/local_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ def _create_vertices(self) -> None:

for code_block in definition:
if ResourceType.TASKS in code_block:
for task in code_block[ResourceType.TASKS]:
self._process_blocks(file_path=file_path, task=task)
tasks = code_block[ResourceType.TASKS]
if tasks: # Check if tasks is not None and not empty
for task in tasks:
self._process_blocks(file_path=file_path, task=task)
else:
self._process_blocks(file_path=file_path, task=code_block)
else:
self._process_blocks(file_path=file_path, task=code_block)

Expand Down
8 changes: 6 additions & 2 deletions checkov/ansible/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,12 @@ def build_definitions_context(

for code_block in definition:
if ResourceType.TASKS in code_block:
for task in code_block[ResourceType.TASKS]:
_process_blocks(definition_raw=definition_raw, file_path_context=file_path_context, task=task)
tasks = code_block[ResourceType.TASKS]
if tasks: # Check if tasks is not empty
for task in tasks:
_process_blocks(definition_raw=definition_raw, file_path_context=file_path_context, task=task)
else:
_process_blocks(definition_raw=definition_raw, file_path_context=file_path_context, task=code_block)
else:
_process_blocks(definition_raw=definition_raw, file_path_context=file_path_context, task=code_block)

Expand Down
3 changes: 2 additions & 1 deletion checkov/arm/graph_builder/graph_components/block_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@

@dataclass
class BlockType(CommonBlockType):
PARAMETER: Literal["parameter"] = "parameter"
PARAMETER: Literal["parameters"] = "parameters"
VARIABLE: Literal["variables"] = "variables"
69 changes: 66 additions & 3 deletions checkov/arm/graph_builder/local_graph.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from __future__ import annotations

import logging
import re
from typing import Any, TYPE_CHECKING

from checkov.arm.graph_builder.graph_components.block_types import BlockType
from checkov.arm.graph_builder.graph_components.blocks import ArmBlock
from checkov.arm.graph_builder.variable_rendering.renderer import ArmVariableRenderer
from checkov.arm.utils import ArmElements
from checkov.common.graph.graph_builder import CustomAttributes
from checkov.common.graph.graph_builder import CustomAttributes, Edge
from checkov.common.graph.graph_builder.local_graph import LocalGraph
from checkov.common.util.consts import START_LINE, END_LINE
from checkov.common.util.data_structures_utils import pickle_deepcopy
Expand All @@ -21,23 +23,57 @@ def __init__(self, definitions: dict[str, dict[str, Any]]) -> None:
self.vertices: list[ArmBlock] = []
self.definitions = definitions
self.vertices_by_path_and_id: dict[tuple[str, str], int] = {}
self.vertices_by_name: dict[str, int] = {}

def build_graph(self, render_variables: bool = False) -> None:
self._create_vertices()
logging.debug(f"[ArmLocalGraph] created {len(self.vertices)} vertices")

self._create_edges()
logging.debug(f"[ArmLocalGraph] created {len(self.edges)} edges")
if render_variables:
renderer = ArmVariableRenderer(self)
renderer.render_variables_from_local_graph()

def _create_vertices(self) -> None:
for file_path, definition in self.definitions.items():
self._create_parameter_vertices(file_path=file_path, parameters=definition.get(ArmElements.PARAMETERS))
self._create_resource_vertices(file_path=file_path, resources=definition.get(ArmElements.RESOURCES))
self._create_variables_vertices(file_path=file_path, variables=definition.get(ArmElements.VARIABLES))

for i, vertex in enumerate(self.vertices):
self.vertices_by_block_type[vertex.block_type].append(i)
self.vertices_block_name_map[vertex.block_type][vertex.name].append(i)
self.vertices_by_path_and_id[(vertex.path, vertex.id)] = i
self.vertices_by_name[vertex.name] = i

self.in_edges[i] = []
self.out_edges[i] = []

def _create_variables_vertices(self, file_path: str, variables: dict[str, dict[str, Any]] | None) -> None:
if not variables:
return

for name, conf in variables.items():
if name in [START_LINE, END_LINE]:
continue
if not isinstance(conf, dict):
full_conf = {"value": pickle_deepcopy(conf)}
else:
full_conf = conf
config = pickle_deepcopy(full_conf)
attributes = pickle_deepcopy(full_conf)

self.vertices.append(
ArmBlock(
name=name,
config=config,
path=file_path,
block_type=BlockType.VARIABLE,
attributes=attributes,
id=f"{BlockType.VARIABLE}.{name}",
)
)

def _create_parameter_vertices(self, file_path: str, parameters: dict[str, dict[str, Any]] | None) -> None:
if not parameters:
Expand Down Expand Up @@ -90,8 +126,35 @@ def _create_resource_vertices(self, file_path: str, resources: list[dict[str, An
)

def _create_edges(self) -> None:
# no edges yet
pass
self._create_vars_and_parameters_edges()
# todo add explicit references edges

def _create_edge(self, element_name: str, origin_vertex_index: int, label: str) -> None:
vertex_name = element_name
if "." in vertex_name:
# special case for bicep and arm elements, when properties are accessed
vertex_name = vertex_name.split(".")[0]

dest_vertex_index = self.vertices_by_name.get(vertex_name)
if dest_vertex_index or dest_vertex_index == 0:
if origin_vertex_index == dest_vertex_index:
return
edge = Edge(origin_vertex_index, dest_vertex_index, label)
self.edges.append(edge)
self.out_edges[origin_vertex_index].append(edge)
self.in_edges[dest_vertex_index].append(edge)

def _create_vars_and_parameters_edges(self) -> None:
pattern = r"(variables|parameters)\('(\w+)'\)"
for origin_vertex_index, vertex in enumerate(self.vertices):
for attr_key, attr_value in vertex.attributes.items():
if not isinstance(attr_value, str):
continue
if ArmElements.VARIABLES in attr_value or ArmElements.PARAMETERS in attr_value:
matches = re.findall(pattern, attr_value)
for match in matches:
var_name = match[1]
self._create_edge(var_name, origin_vertex_index, attr_key)

def update_vertices_configs(self) -> None:
# not used
Expand Down
Empty file.
64 changes: 64 additions & 0 deletions checkov/arm/graph_builder/variable_rendering/renderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any

from checkov.arm.graph_builder.graph_components.block_types import BlockType
from checkov.common.graph.graph_builder import Edge
from checkov.common.graph.graph_builder.utils import adjust_value
from checkov.common.graph.graph_builder.variable_rendering.renderer import VariableRenderer
from checkov.common.util.data_structures_utils import pickle_deepcopy

if TYPE_CHECKING:
from checkov.arm.graph_builder.local_graph import ArmLocalGraph


class ArmVariableRenderer(VariableRenderer["ArmLocalGraph"]):
def __init__(self, local_graph: ArmLocalGraph) -> None:
super().__init__(local_graph)

def _render_variables_from_vertices(self) -> None:
# need to add rendering to function like format, reference etc
pass

def evaluate_vertex_attribute_from_edge(self, edge_list: list[Edge]) -> None:
origin_vertex_attributes = self.local_graph.vertices[edge_list[0].origin].attributes
val_to_eval = pickle_deepcopy(origin_vertex_attributes.get(edge_list[0].label, ""))
attr_path = None
for edge in edge_list:
attr_path, attr_value = self.extract_dest_attribute_path_and_value(dest_index=edge.dest,
origin_value=val_to_eval)
'''if the arg start with '[parameters'/ '[variables' its mean we need to eval the all attribute
like here - "addressPrefix": "[parameters('subnetAddressPrefix')]" '''
if len(edge_list) == 1 and val_to_eval.startswith(("[parameters", "[variables")):
val_to_eval = attr_value
continue
'''
if the value i need to eval is part of the full attribute like "[format('{0}/{1}', parameters('vnetName'), variables('subnetName'))]"
or "[resourceId('Microsoft.Network/networkProfiles', variables('networkProfileName'))]".
vertices[edge.dest].id = variables.networkProfileName -> variables('networkProfileName')
'''
val_to_replace = self.local_graph.vertices[edge.dest].id.replace(".", "('") + "')"
val_to_eval = val_to_eval.replace(val_to_replace, attr_value)

self.local_graph.update_vertex_attribute(
vertex_index=edge_list[0].origin,
attribute_key=edge_list[0].label,
attribute_value=val_to_eval,
change_origin_id=edge_list[0].dest,
attribute_at_dest=attr_path,
)

def extract_dest_attribute_path_and_value(self, dest_index: int, origin_value: Any) -> tuple[str, Any] | tuple[None, None]:
vertex = self.local_graph.vertices[dest_index]
if vertex.block_type == BlockType.PARAMETER:
new_value = vertex.attributes.get("defaultValue")
if new_value:
new_value = adjust_value(element_name=origin_value, value=new_value)
return "defaultValue", new_value
elif vertex.block_type == BlockType.VARIABLE:
new_value = adjust_value(element_name=origin_value, value=vertex.attributes["value"])
return "value", new_value
return None, None

def evaluate_non_rendered_values(self) -> None:
pass
2 changes: 1 addition & 1 deletion checkov/bicep/graph_builder/local_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
from checkov.bicep.graph_builder.graph_components.block_types import BlockType
from checkov.bicep.graph_builder.graph_components.blocks import BicepBlock
from checkov.bicep.graph_builder.variable_rendering.renderer import BicepVariableRenderer
from checkov.bicep.utils import adjust_value
from checkov.common.graph.graph_builder.graph_components.edge import Edge
from checkov.common.graph.graph_builder.local_graph import LocalGraph
from checkov.common.graph.graph_builder.utils import filter_sub_keys
from checkov.common.graph.graph_builder.utils import adjust_value
from checkov.common.util.data_structures_utils import pickle_deepcopy
from checkov.common.util.type_forcers import force_int

Expand Down
2 changes: 1 addition & 1 deletion checkov/bicep/graph_builder/variable_rendering/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from pycep.transformer import BicepElement

from checkov.bicep.graph_builder.graph_components.block_types import BlockType
from checkov.bicep.utils import adjust_value
from checkov.common.graph.graph_builder import Edge
from checkov.common.graph.graph_builder.utils import adjust_value
from checkov.common.graph.graph_builder.variable_rendering.renderer import VariableRenderer
from checkov.common.util.data_structures_utils import pickle_deepcopy

Expand Down
25 changes: 1 addition & 24 deletions checkov/bicep/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import re
from collections.abc import Collection
from pathlib import Path
from typing import Any, TYPE_CHECKING
from typing import TYPE_CHECKING

from checkov.common.runners.base_runner import filter_ignored_paths
from checkov.runner_filter import RunnerFilter
Expand Down Expand Up @@ -88,26 +88,3 @@ def create_definitions(
logging.warning(f"[bicep] found errors while parsing definitions: {parsing_errors}")

return definitions, definitions_raw


def adjust_value(element_name: str, value: Any) -> Any:
"""Adjusts the value, if the 'element_name' references a nested key
Ex:
element_name = publicKey.keyData
value = {"keyData": "key-data", "path": "path"}
returns new_value = "key-data"
"""

if "." in element_name and isinstance(value, dict):
key_parts = element_name.split(".")
new_value = value.get(key_parts[1])

if new_value is None:
# couldn't find key in in value object
return None

return adjust_value(".".join(key_parts[1:]), new_value)

return value
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def _get_fixes_for_file(

logging.debug(f'Response from fixes API: {request.data}')

fixes: list[dict[str, Any]] = json.loads(request.data) if request.data else None
fixes: list[dict[str, Any]] | None = json.loads(request.data) if request.data else None
if not fixes or not isinstance(fixes, list):
logging.warning(f'Unexpected fixes API response for file {filename}; skipping fixes for this file')
return None
Expand Down
6 changes: 5 additions & 1 deletion checkov/common/bridgecrew/platform_errors.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from typing import List


class PlatformConnectionError(Exception):
def __init__(self, message: str) -> None:
self.message = message
Expand All @@ -16,8 +19,9 @@ def __str__(self) -> str:


class ModuleNotEnabledError(Exception):
def __init__(self, message: str) -> None:
def __init__(self, message: str, unsupported_frameworks: List[str]) -> None:
self.message = message
self.unsupported_frameworks = unsupported_frameworks

def __str__(self) -> str:
return f"ModuleNotEnabledError: {self.message}"
2 changes: 1 addition & 1 deletion checkov/common/bridgecrew/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def reduce_scan_reports(scan_reports: list[Report], on_prem: Optional[bool] = Fa
continue
reduced_keys = secrets_check_reduced_keys if check_type == CheckType.SECRETS else check_reduced_keys
if on_prem:
reduced_keys = tuple(k for k in reduced_keys if k != 'code_block')
reduced_keys = tuple(k for k in reduced_keys if k != 'code_block') # type: ignore
reduced_scan_reports[check_type] = \
{
"checks": {
Expand Down
Loading

0 comments on commit 3bb9afe

Please sign in to comment.