Skip to content

Commit

Permalink
feat(terraform): support s3 bucket name for references in graph (#6134)
Browse files Browse the repository at this point in the history
* feature(terraform): support s3 bucket name for references

* remove commented block

* add comments and improve naming

* add test case

* fix array update

* fix mypy issue

* add test case, support locals, discard for non resource types

* remove env var

* add none validation

* resolve mypy issue in unrelated file

* add typedDict for better typing

---------

Co-authored-by: Steve Vaknin <svaknin@paloaltonetworks.com>
  • Loading branch information
SteveVaknin and Steve Vaknin committed Apr 3, 2024
1 parent 17b4f05 commit 3caa971
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
40 changes: 40 additions & 0 deletions checkov/terraform/graph_builder/local_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

MODULE_RESERVED_ATTRIBUTES = ("source", "version")
CROSS_VARIABLE_EDGE_PREFIX = '[cross-variable] '
S3_BUCKET_RESOURCE_NAME = "aws_s3_bucket"
S3_BUCKET_REFERENCE_ATTRIBUTE = "bucket"


class Undetermined(TypedDict):
Expand All @@ -46,6 +48,11 @@ class Undetermined(TypedDict):
variable_vertex_id: int


class S3ConnectedResources(TypedDict):
bucket_resource_index: int | None
referenced_vertices: List[Edge]


class TerraformLocalGraph(LocalGraph[TerraformBlock]):
def __init__(self, module: Module) -> None:
super().__init__()
Expand Down Expand Up @@ -94,6 +101,11 @@ def build_graph(self, render_variables: bool) -> None:
edges_count = len(self.edges)
self._build_cross_variable_edges()
logging.info(f"Found {len(self.edges) - edges_count} cross variable edges")
# building S3 edges by name for terraform graph
logging.info("Building S3 edges name references")
edges_count = len(self.edges)
self._build_s3_name_reference_edges()
logging.info(f"Found {len(self.edges) - edges_count} S3 name references edges")
else:
self.update_vertices_fields()

Expand Down Expand Up @@ -323,6 +335,34 @@ def _build_cross_variable_edges(self) -> None:
modules = vertex.breadcrumbs.get(CustomAttributes.SOURCE_MODULE, [])
self._build_edges_for_vertex(origin_node_index, vertex, aliases, resources_types, True, modules)

def _build_s3_name_reference_edges(self) -> None:
# Supporting reference by name of S3 bucket
resources_types = self.get_resources_types_in_graph()
if S3_BUCKET_RESOURCE_NAME not in resources_types:
return
# Find all the edges leading to S3 bucket and their references
s3_buckets_mapping: Dict[int, S3ConnectedResources] = {}
for origin_node_index, referenced_vertices in self.out_edges.items():
vertex = self.vertices[origin_node_index]
if vertex.block_type != BlockType.RESOURCE:
continue
for referenced_vertice in referenced_vertices:
if referenced_vertice.label == S3_BUCKET_REFERENCE_ATTRIBUTE:
current = s3_buckets_mapping.get(referenced_vertice.dest, {"bucket_resource_index": None, "referenced_vertices": list()})
if vertex.id.startswith(f"{S3_BUCKET_RESOURCE_NAME}."):
current["bucket_resource_index"] = origin_node_index
else:
current["referenced_vertices"].append(referenced_vertice)
s3_buckets_mapping[referenced_vertice.dest] = current

# Create new edges of the found connections
for destination, mapping in s3_buckets_mapping.items():
if self.vertices[destination].block_type in [BlockType.VARIABLE, BlockType.LOCALS]:
if mapping["bucket_resource_index"] is None:
continue
for reference_vertex in mapping["referenced_vertices"]:
self.create_edge(mapping["bucket_resource_index"], reference_vertex.origin, S3_BUCKET_REFERENCE_ATTRIBUTE, True)

def create_edge(self, origin_vertex_index: int, dest_vertex_index: int, label: str,
cross_variable_edges: bool = False) -> bool:
if origin_vertex_index == dest_vertex_index:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pass:
- "aws_s3_bucket.enabled_var"
- 'aws_s3_bucket.this["test-bucket1"]'
- 'aws_s3_bucket.this["test-bucket2"]'
- "aws_s3_bucket.ref_by_name"
- "aws_s3_bucket.ref_by_name_local"
fail:
- "aws_s3_bucket.default"
- "aws_s3_bucket.disabled"
Expand Down
33 changes: 33 additions & 0 deletions tests/terraform/graph/checks/resources/S3BucketVersioning/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,39 @@ resource "aws_s3_bucket" "legacy_syntax" {
}
}

# Reference by name
variable "bucket_name" {
}

resource "aws_s3_bucket" "ref_by_name" {
bucket = var.bucket_name
}

resource "aws_s3_bucket_versioning" "aws_bucket_versioning" {
bucket = var.bucket_name
versioning_configuration {
status = "Enabled"
}
}

variable "bucket_name_2" {
}

locals {
bucketName = var.bucket_name_2
}

resource "aws_s3_bucket" "ref_by_name_local" {
bucket = local.bucketName
}

resource "aws_s3_bucket_versioning" "aws_bucket_versioning_local" {
bucket = local.bucketName
versioning_configuration {
status = "Enabled"
}
}

# fail

resource "aws_s3_bucket" "default" {
Expand Down

0 comments on commit 3caa971

Please sign in to comment.