From dc123b559decc1fddede92165fbd7592a9b44abf Mon Sep 17 00:00:00 2001 From: Taylor Date: Thu, 5 Oct 2023 12:12:05 -0700 Subject: [PATCH 1/5] add provisioner --- checkov/terraform/plan_parser.py | 5 + .../resources/plan_provisioners/tfplan.json | 405 ++++++++++++++++++ tests/terraform/parser/test_plan_parser.py | 9 + 3 files changed, 419 insertions(+) create mode 100644 tests/terraform/parser/resources/plan_provisioners/tfplan.json diff --git a/checkov/terraform/plan_parser.py b/checkov/terraform/plan_parser.py index 576a651d358..8beeae2bd09 100644 --- a/checkov/terraform/plan_parser.py +++ b/checkov/terraform/plan_parser.py @@ -15,6 +15,7 @@ TF_PLAN_RESOURCE_ADDRESS = CustomAttributes.TF_RESOURCE_ADDRESS TF_PLAN_RESOURCE_CHANGE_ACTIONS = "__change_actions__" TF_PLAN_RESOURCE_CHANGE_KEYS = "__change_keys__" +TF_PLAN_RESOURCE_PROVISIONERS = "provisioners" RESOURCE_TYPES_JSONIFY = { "aws_batch_job_definition": "container_properties", @@ -165,6 +166,10 @@ def _prepare_resource_block( resource_conf[TF_PLAN_RESOURCE_CHANGE_ACTIONS] = changes.get("change", {}).get("actions") or [] resource_conf[TF_PLAN_RESOURCE_CHANGE_KEYS] = changes.get(TF_PLAN_RESOURCE_CHANGE_KEYS) or [] + provisioners = conf.get(TF_PLAN_RESOURCE_PROVISIONERS) if conf else None + if provisioners: + resource_conf[TF_PLAN_RESOURCE_PROVISIONERS] = provisioners + resource_block[resource_type][resource.get("name", "default")] = resource_conf prepared = True return resource_block, block_type, prepared diff --git a/tests/terraform/parser/resources/plan_provisioners/tfplan.json b/tests/terraform/parser/resources/plan_provisioners/tfplan.json new file mode 100644 index 00000000000..fe31076b164 --- /dev/null +++ b/tests/terraform/parser/resources/plan_provisioners/tfplan.json @@ -0,0 +1,405 @@ +{ + "format_version": "1.2", + "terraform_version": "1.5.7", + "planned_values": { + "root_module": { + "resources": [ + { + "address": "aws_db_instance.default", + "mode": "managed", + "type": "aws_db_instance", + "name": "default", + "provider_name": "registry.terraform.io/hashicorp/aws", + "schema_version": 2, + "values": { + "allocated_storage": 10, + "allow_major_version_upgrade": null, + "apply_immediately": false, + "auto_minor_version_upgrade": true, + "blue_green_update": [], + "copy_tags_to_snapshot": false, + "custom_iam_instance_profile": null, + "customer_owned_ip_enabled": null, + "db_name": "mydb", + "delete_automated_backups": true, + "deletion_protection": null, + "domain": null, + "domain_iam_role_name": null, + "enabled_cloudwatch_logs_exports": null, + "engine": "mysql", + "engine_version": "5.7", + "final_snapshot_identifier": null, + "iam_database_authentication_enabled": null, + "instance_class": "db.t3.micro", + "manage_master_user_password": null, + "max_allocated_storage": null, + "monitoring_interval": 0, + "parameter_group_name": "default.mysql5.7", + "password": "foobarbaz", + "performance_insights_enabled": false, + "publicly_accessible": false, + "replicate_source_db": null, + "restore_to_point_in_time": [], + "s3_import": [], + "skip_final_snapshot": true, + "storage_encrypted": null, + "tags": null, + "timeouts": null, + "username": "foo" + }, + "sensitive_values": { + "blue_green_update": [], + "listener_endpoint": [], + "master_user_secret": [], + "replicas": [], + "restore_to_point_in_time": [], + "s3_import": [], + "tags_all": {}, + "vpc_security_group_ids": [] + } + }, + { + "address": "terraform_data.bad2", + "mode": "managed", + "type": "terraform_data", + "name": "bad2", + "provider_name": "terraform.io/builtin/terraform", + "schema_version": 0, + "values": { + "input": null, + "output": null, + "triggers_replace": null + }, + "sensitive_values": {} + } + ] + } + }, + "resource_changes": [ + { + "address": "aws_db_instance.default", + "mode": "managed", + "type": "aws_db_instance", + "name": "default", + "provider_name": "registry.terraform.io/hashicorp/aws", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "allocated_storage": 10, + "allow_major_version_upgrade": null, + "apply_immediately": false, + "auto_minor_version_upgrade": true, + "blue_green_update": [], + "copy_tags_to_snapshot": false, + "custom_iam_instance_profile": null, + "customer_owned_ip_enabled": null, + "db_name": "mydb", + "delete_automated_backups": true, + "deletion_protection": null, + "domain": null, + "domain_iam_role_name": null, + "enabled_cloudwatch_logs_exports": null, + "engine": "mysql", + "engine_version": "5.7", + "final_snapshot_identifier": null, + "iam_database_authentication_enabled": null, + "instance_class": "db.t3.micro", + "manage_master_user_password": null, + "max_allocated_storage": null, + "monitoring_interval": 0, + "parameter_group_name": "default.mysql5.7", + "password": "foobarbaz", + "performance_insights_enabled": false, + "publicly_accessible": false, + "replicate_source_db": null, + "restore_to_point_in_time": [], + "s3_import": [], + "skip_final_snapshot": true, + "storage_encrypted": null, + "tags": null, + "timeouts": null, + "username": "foo" + }, + "after_unknown": { + "address": true, + "arn": true, + "availability_zone": true, + "backup_retention_period": true, + "backup_target": true, + "backup_window": true, + "blue_green_update": [], + "ca_cert_identifier": true, + "character_set_name": true, + "db_subnet_group_name": true, + "endpoint": true, + "engine_version_actual": true, + "hosted_zone_id": true, + "id": true, + "identifier": true, + "identifier_prefix": true, + "iops": true, + "kms_key_id": true, + "latest_restorable_time": true, + "license_model": true, + "listener_endpoint": true, + "maintenance_window": true, + "master_user_secret": true, + "master_user_secret_kms_key_id": true, + "monitoring_role_arn": true, + "multi_az": true, + "nchar_character_set_name": true, + "network_type": true, + "option_group_name": true, + "performance_insights_kms_key_id": true, + "performance_insights_retention_period": true, + "port": true, + "replica_mode": true, + "replicas": true, + "resource_id": true, + "restore_to_point_in_time": [], + "s3_import": [], + "snapshot_identifier": true, + "status": true, + "storage_throughput": true, + "storage_type": true, + "tags_all": true, + "timezone": true, + "vpc_security_group_ids": true + }, + "before_sensitive": false, + "after_sensitive": { + "blue_green_update": [], + "listener_endpoint": [], + "master_user_secret": [], + "password": true, + "replicas": [], + "restore_to_point_in_time": [], + "s3_import": [], + "tags_all": {}, + "vpc_security_group_ids": [] + } + } + }, + { + "address": "terraform_data.bad2", + "mode": "managed", + "type": "terraform_data", + "name": "bad2", + "provider_name": "terraform.io/builtin/terraform", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "input": null, + "output": null, + "triggers_replace": null + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + } + ], + "configuration": { + "provider_config": { + "aws": { + "name": "aws", + "full_name": "registry.terraform.io/hashicorp/aws", + "version_constraint": "~> 5.0", + "expressions": { + "access_key": { + "constant_value": "mock_access_key" + }, + "region": { + "constant_value": "us-east-1" + }, + "secret_key": { + "constant_value": "mock_secret_key" + }, + "skip_credentials_validation": { + "constant_value": true + }, + "skip_metadata_api_check": { + "constant_value": true + }, + "skip_requesting_account_id": { + "constant_value": true + } + } + }, + "terraform": { + "name": "terraform", + "full_name": "terraform.io/builtin/terraform" + } + }, + "root_module": { + "resources": [ + { + "address": "aws_db_instance.default", + "mode": "managed", + "type": "aws_db_instance", + "name": "default", + "provider_config_key": "aws", + "expressions": { + "allocated_storage": { + "constant_value": 10 + }, + "db_name": { + "constant_value": "mydb" + }, + "engine": { + "constant_value": "mysql" + }, + "engine_version": { + "constant_value": "5.7" + }, + "instance_class": { + "constant_value": "db.t3.micro" + }, + "parameter_group_name": { + "constant_value": "default.mysql5.7" + }, + "password": { + "constant_value": "foobarbaz" + }, + "skip_final_snapshot": { + "constant_value": true + }, + "username": { + "constant_value": "foo" + } + }, + "schema_version": 2 + }, + { + "address": "aws_instance.bad", + "mode": "managed", + "type": "aws_instance", + "name": "bad", + "provider_config_key": "aws", + "provisioners": [ + { + "type": "local-exec", + "expressions": { + "command": { + "references": [ + "self.private_ip", + "self" + ] + } + } + } + ], + "expressions": { + "ami": { + "references": [ + "data.aws_ami.ubuntu.id", + "data.aws_ami.ubuntu" + ] + }, + "instance_type": { + "constant_value": "t3.micro" + } + }, + "schema_version": 1 + }, + { + "address": "aws_instance.web", + "mode": "managed", + "type": "aws_instance", + "name": "web", + "provider_config_key": "aws", + "expressions": { + "ami": { + "references": [ + "data.aws_ami.ubuntu.id", + "data.aws_ami.ubuntu" + ] + }, + "instance_type": { + "constant_value": "t3.micro" + }, + "tags": { + "constant_value": { + "Name": "HelloWorld" + } + } + }, + "schema_version": 1 + }, + { + "address": "terraform_data.bad2", + "mode": "managed", + "type": "terraform_data", + "name": "bad2", + "provider_config_key": "terraform", + "provisioners": [ + { + "type": "local-exec", + "expressions": { + "command": { + "constant_value": "open WFH, '>completed.txt' and print WFH scalar localtime" + }, + "interpreter": { + "constant_value": [ + "perl", + "-e" + ] + } + } + } + ], + "schema_version": 0 + }, + { + "address": "data.aws_ami.ubuntu", + "mode": "data", + "type": "aws_ami", + "name": "ubuntu", + "provider_config_key": "aws", + "expressions": { + "filter": [ + { + "name": { + "constant_value": "name" + }, + "values": { + "constant_value": [ + "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*" + ] + } + }, + { + "name": { + "constant_value": "virtualization-type" + }, + "values": { + "constant_value": [ + "hvm" + ] + } + } + ], + "most_recent": { + "constant_value": true + }, + "owners": { + "constant_value": [ + "099720109477" + ] + } + }, + "schema_version": 0 + } + ] + } + }, + "timestamp": "2023-09-30T04:54:35Z" +} diff --git a/tests/terraform/parser/test_plan_parser.py b/tests/terraform/parser/test_plan_parser.py index 94fbfe6c942..9424b4eab89 100644 --- a/tests/terraform/parser/test_plan_parser.py +++ b/tests/terraform/parser/test_plan_parser.py @@ -51,6 +51,15 @@ def test_encodings(self): tf_definition, _ = parse_tf_plan(plan_path, {}) self.assertEqual(list(tf_definition['resource'][0].keys())[0], "aws_s3_bucket") + def test_provisioners(self): + current_dir = os.path.dirname(os.path.realpath(__file__)) + valid_plan_path = current_dir + "/resources/plan_provisioners/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.assertTrue(resource_attributes["provisioners"]) + def test_large_file(mocker: MockerFixture): # given From 01f7a232a02315c8ead8c61581b96d08ebe5b64f Mon Sep 17 00:00:00 2001 From: Taylor Date: Thu, 5 Oct 2023 12:41:13 -0700 Subject: [PATCH 2/5] fix test --- tests/terraform/parser/test_plan_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/terraform/parser/test_plan_parser.py b/tests/terraform/parser/test_plan_parser.py index 9424b4eab89..2645841cbbe 100644 --- a/tests/terraform/parser/test_plan_parser.py +++ b/tests/terraform/parser/test_plan_parser.py @@ -55,10 +55,10 @@ def test_provisioners(self): current_dir = os.path.dirname(os.path.realpath(__file__)) valid_plan_path = current_dir + "/resources/plan_provisioners/tfplan.json" tf_definition, _ = parse_tf_plan(valid_plan_path, {}) - file_resource_definition = tf_definition['resource'][0] + file_resource_definition = tf_definition['resource'][1] resource_definition = next(iter(file_resource_definition.values())) resource_attributes = next(iter(resource_definition.values())) - self.assertTrue(resource_attributes["provisioners"]) + self.assertTrue(resource_attributes['provisioners']) def test_large_file(mocker: MockerFixture): From 8a9e33e58d4dd4f42f7368d1662c8d984d9b45cb Mon Sep 17 00:00:00 2001 From: Taylor Date: Thu, 19 Oct 2023 01:21:28 -0700 Subject: [PATCH 3/5] Transform data to match TF --- checkov/terraform/plan_parser.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/checkov/terraform/plan_parser.py b/checkov/terraform/plan_parser.py index 8beeae2bd09..c15db00b3ec 100644 --- a/checkov/terraform/plan_parser.py +++ b/checkov/terraform/plan_parser.py @@ -167,8 +167,9 @@ def _prepare_resource_block( resource_conf[TF_PLAN_RESOURCE_CHANGE_KEYS] = changes.get(TF_PLAN_RESOURCE_CHANGE_KEYS) or [] provisioners = conf.get(TF_PLAN_RESOURCE_PROVISIONERS) if conf else None + #resource_conf["provisioner"] = provisioners if provisioners: - resource_conf[TF_PLAN_RESOURCE_PROVISIONERS] = provisioners + resource_conf["provisioner"] = _get_provisioner(provisioners) resource_block[resource_type][resource.get("name", "default")] = resource_conf prepared = True @@ -347,3 +348,15 @@ def _clean_simple_type_list(value_list: List[Any]) -> List[Any]: if lower_case_value == "false": value_list[i] = False return value_list + + +def _get_provisioner(input_data: List[Any]) -> List[Any]: + result = [] + for item in input_data: + key = item['type'] + command_value = item['expressions']['command'] + if not isinstance(command_value, list): + command_value = [command_value] + transformed_item = {key: {'command': command_value}} + result.append(transformed_item) + return result From f51ab255d33eb6805066ead7aa246e1f50776bd9 Mon Sep 17 00:00:00 2001 From: Taylor Date: Thu, 19 Oct 2023 01:27:28 -0700 Subject: [PATCH 4/5] Remove debug code comment --- checkov/terraform/plan_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/checkov/terraform/plan_parser.py b/checkov/terraform/plan_parser.py index c15db00b3ec..04c92718e92 100644 --- a/checkov/terraform/plan_parser.py +++ b/checkov/terraform/plan_parser.py @@ -167,7 +167,6 @@ def _prepare_resource_block( resource_conf[TF_PLAN_RESOURCE_CHANGE_KEYS] = changes.get(TF_PLAN_RESOURCE_CHANGE_KEYS) or [] provisioners = conf.get(TF_PLAN_RESOURCE_PROVISIONERS) if conf else None - #resource_conf["provisioner"] = provisioners if provisioners: resource_conf["provisioner"] = _get_provisioner(provisioners) From d6ef63ad5a82b9064b9345e25c6a9b0855f02abd Mon Sep 17 00:00:00 2001 From: Taylor Date: Thu, 19 Oct 2023 01:37:33 -0700 Subject: [PATCH 5/5] fix test --- tests/terraform/parser/test_plan_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/terraform/parser/test_plan_parser.py b/tests/terraform/parser/test_plan_parser.py index 2645841cbbe..f0e355aabf4 100644 --- a/tests/terraform/parser/test_plan_parser.py +++ b/tests/terraform/parser/test_plan_parser.py @@ -58,7 +58,7 @@ def test_provisioners(self): file_resource_definition = tf_definition['resource'][1] resource_definition = next(iter(file_resource_definition.values())) resource_attributes = next(iter(resource_definition.values())) - self.assertTrue(resource_attributes['provisioners']) + self.assertTrue(resource_attributes['provisioner']) def test_large_file(mocker: MockerFixture):