From a78979e8f29e5f279d36ca945e72cc25704aedc2 Mon Sep 17 00:00:00 2001 From: Manu Chandrasekhar Date: Fri, 9 Aug 2024 17:26:42 -0400 Subject: [PATCH 1/4] feat: add support for awscc provider secrets check --- checkov/terraform/checks/provider/__init__.py | 3 +- .../checks/provider/awscc/__init__.py | 5 ++ .../checks/provider/awscc/credentials.py | 37 ++++++++++ .../checks/provider/awscc/__init__.py | 0 .../checks/provider/awscc/test_credentials.py | 74 +++++++++++++++++++ 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 checkov/terraform/checks/provider/awscc/__init__.py create mode 100644 checkov/terraform/checks/provider/awscc/credentials.py create mode 100644 tests/terraform/checks/provider/awscc/__init__.py create mode 100644 tests/terraform/checks/provider/awscc/test_credentials.py diff --git a/checkov/terraform/checks/provider/__init__.py b/checkov/terraform/checks/provider/__init__.py index 798e90afcd..56037750d2 100644 --- a/checkov/terraform/checks/provider/__init__.py +++ b/checkov/terraform/checks/provider/__init__.py @@ -1,6 +1,7 @@ from checkov.terraform.checks.provider.aws import * # noqa -from checkov.terraform.checks.provider.linode import * # noqa +from checkov.terraform.checks.provider.awscc import * # noqa from checkov.terraform.checks.provider.bridgecrew import * # noqa +from checkov.terraform.checks.provider.linode import * # noqa from checkov.terraform.checks.provider.oci import * # noqa from checkov.terraform.checks.provider.openstack import * # noqa from checkov.terraform.checks.provider.panos import * # noqa diff --git a/checkov/terraform/checks/provider/awscc/__init__.py b/checkov/terraform/checks/provider/awscc/__init__.py new file mode 100644 index 0000000000..c619945591 --- /dev/null +++ b/checkov/terraform/checks/provider/awscc/__init__.py @@ -0,0 +1,5 @@ +from os.path import dirname, basename, isfile, join +import glob + +modules = glob.glob(join(dirname(__file__), "*.py")) +__all__ = [basename(f)[:-3] for f in modules if isfile(f) and not f.endswith("__init__.py")] diff --git a/checkov/terraform/checks/provider/awscc/credentials.py b/checkov/terraform/checks/provider/awscc/credentials.py new file mode 100644 index 0000000000..f73dd0e40d --- /dev/null +++ b/checkov/terraform/checks/provider/awscc/credentials.py @@ -0,0 +1,37 @@ +import re +from typing import Any, Dict, List + +from checkov.common.models.consts import access_key_pattern, secret_key_pattern +from checkov.common.models.enums import CheckCategories, CheckResult +from checkov.terraform.checks.provider.base_check import BaseProviderCheck + + +class AWSCCCredentials(BaseProviderCheck): + def __init__(self) -> None: + name = "Ensure no hard coded AWS access key and secret key exists in provider" + id = "CKV_AWSCC_41" + supported_provider = ["awscc"] + categories = [CheckCategories.SECRETS] + super().__init__(name=name, id=id, categories=categories, supported_provider=supported_provider) + + def scan_provider_conf(self, conf: Dict[str, List[Any]]) -> CheckResult: + """ + see: https://registry.terraform.io/providers/hashicorp/awscc/latest/docs#authentication + """ + result = CheckResult.PASSED + if self.secret_found(conf, "access_key", access_key_pattern): + result = CheckResult.FAILED + if self.secret_found(conf, "secret_key", secret_key_pattern): + result = CheckResult.FAILED + return result + + def secret_found(self, conf: Dict[str, List[Any]], field: str, pattern: str) -> bool: + if field in conf.keys(): + value = conf[field][0] + if isinstance(value, str) and re.match(pattern, value) is not None: + conf[f'{self.id}_secret_{field}'] = value + return True + return False + + +check = AWSCCCredentials() diff --git a/tests/terraform/checks/provider/awscc/__init__.py b/tests/terraform/checks/provider/awscc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/terraform/checks/provider/awscc/test_credentials.py b/tests/terraform/checks/provider/awscc/test_credentials.py new file mode 100644 index 0000000000..af8d8ae433 --- /dev/null +++ b/tests/terraform/checks/provider/awscc/test_credentials.py @@ -0,0 +1,74 @@ +import unittest + +import hcl2 + +from checkov.terraform.checks.provider.awscc.credentials import check +from checkov.common.models.enums import CheckResult + + +class TestCredentials(unittest.TestCase): + def test_success_empty(self): + hcl_res = hcl2.loads( + """ + provider "awscc" {} + """ + ) + provider_conf = hcl_res["provider"][0]["awscc"] + scan_result = check.scan_provider_conf(conf=provider_conf) + self.assertEqual(CheckResult.PASSED, scan_result) + + def test_success_region(self): + hcl_res = hcl2.loads( + """ + provider "awscc" { + region = "us-west-2" + } + """ + ) + provider_conf = hcl_res["provider"][0]["awscc"] + scan_result = check.scan_provider_conf(conf=provider_conf) + self.assertEqual(CheckResult.PASSED, scan_result) + + def test_failure_both_keys(self): + hcl_res = hcl2.loads( + """ + provider "awscc" { + region = "us-west-2" + access_key = "AKIAIOSFODNN7EXAMPLE" + secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + } + """ + ) + provider_conf = hcl_res["provider"][0]["awscc"] + scan_result = check.scan_provider_conf(conf=provider_conf) + self.assertEqual(CheckResult.FAILED, scan_result) + + def test_failure_access_key(self): + hcl_res = hcl2.loads( + """ + provider "awscc" { + region = "us-west-2" + access_key = "AKIAIOSFODNN7EXAMPLE" + } + """ + ) + provider_conf = hcl_res["provider"][0]["awscc"] + scan_result = check.scan_provider_conf(conf=provider_conf) + self.assertEqual(CheckResult.FAILED, scan_result) + + def test_failure_secret_key(self): + hcl_res = hcl2.loads( + """ + provider "awscc" { + region = "us-west-2" + secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + } + """ + ) + provider_conf = hcl_res["provider"][0]["awscc"] + scan_result = check.scan_provider_conf(conf=provider_conf) + self.assertEqual(CheckResult.FAILED, scan_result) + + +if __name__ == "__main__": + unittest.main() From 7a88b4aed92a2295312376c5a9898960771c8dd1 Mon Sep 17 00:00:00 2001 From: quixoticmonk Date: Sun, 26 Jan 2025 13:56:03 -0500 Subject: [PATCH 2/4] fix: update service provider reference --- checkov/terraform/checks/provider/awscc/credentials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checkov/terraform/checks/provider/awscc/credentials.py b/checkov/terraform/checks/provider/awscc/credentials.py index f73dd0e40d..0feedb9901 100644 --- a/checkov/terraform/checks/provider/awscc/credentials.py +++ b/checkov/terraform/checks/provider/awscc/credentials.py @@ -9,7 +9,7 @@ class AWSCCCredentials(BaseProviderCheck): def __init__(self) -> None: name = "Ensure no hard coded AWS access key and secret key exists in provider" - id = "CKV_AWSCC_41" + id = "CKV_AWS_41" supported_provider = ["awscc"] categories = [CheckCategories.SECRETS] super().__init__(name=name, id=id, categories=categories, supported_provider=supported_provider) From b01ef3df2ab1a2f7295270b18ffed7e7627bf82b Mon Sep 17 00:00:00 2001 From: quixoticmonk Date: Thu, 6 Mar 2025 14:33:49 +0530 Subject: [PATCH 3/4] fix: tests based on framework --- .../awscc/example_Credentials/main.tf | 23 +++++ .../checks/provider/awscc/test_credentials.py | 88 ++++++------------- 2 files changed, 50 insertions(+), 61 deletions(-) create mode 100644 tests/terraform/checks/provider/awscc/example_Credentials/main.tf diff --git a/tests/terraform/checks/provider/awscc/example_Credentials/main.tf b/tests/terraform/checks/provider/awscc/example_Credentials/main.tf new file mode 100644 index 0000000000..41bf7fd487 --- /dev/null +++ b/tests/terraform/checks/provider/awscc/example_Credentials/main.tf @@ -0,0 +1,23 @@ +provider "awscc" { + alias = "pass" + region = "us-west-2" +} + +provider "awscc" { + alias = "fail" + region = "us-west-2" + access_key = "AKIAIOSFODNN7EXAMPLE" + secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" +} + +provider "awscc" { + alias = "fail2" + region = "us-west-2" + access_key = "AKIAIOSFODNN7EXAMPLE" +} + +provider "awscc" { + alias = "fail3" + region = "us-west-2" + secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" +} \ No newline at end of file diff --git a/tests/terraform/checks/provider/awscc/test_credentials.py b/tests/terraform/checks/provider/awscc/test_credentials.py index af8d8ae433..d171a177a6 100644 --- a/tests/terraform/checks/provider/awscc/test_credentials.py +++ b/tests/terraform/checks/provider/awscc/test_credentials.py @@ -1,73 +1,39 @@ +import os import unittest -import hcl2 - +from checkov.runner_filter import RunnerFilter from checkov.terraform.checks.provider.awscc.credentials import check -from checkov.common.models.enums import CheckResult +from checkov.terraform.runner import Runner + +class TestAWSCCCredentials(unittest.TestCase): + def test(self): + runner = Runner() + current_dir = os.path.dirname(os.path.realpath(__file__)) -class TestCredentials(unittest.TestCase): - def test_success_empty(self): - hcl_res = hcl2.loads( - """ - provider "awscc" {} - """ - ) - provider_conf = hcl_res["provider"][0]["awscc"] - scan_result = check.scan_provider_conf(conf=provider_conf) - self.assertEqual(CheckResult.PASSED, scan_result) + test_files_dir = current_dir + "/example_Credentials" + report = runner.run(root_folder=test_files_dir, runner_filter=RunnerFilter(checks=[check.id])) + summary = report.get_summary() - def test_success_region(self): - hcl_res = hcl2.loads( - """ - provider "awscc" { - region = "us-west-2" - } - """ - ) - provider_conf = hcl_res["provider"][0]["awscc"] - scan_result = check.scan_provider_conf(conf=provider_conf) - self.assertEqual(CheckResult.PASSED, scan_result) + passing_resources = { + "provider.awscc.pass" + } + failing_resources = { + "provider.awscc.fail", + "provider.awscc.fail2", + "provider.awscc.fail3", + } - def test_failure_both_keys(self): - hcl_res = hcl2.loads( - """ - provider "awscc" { - region = "us-west-2" - access_key = "AKIAIOSFODNN7EXAMPLE" - secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" - } - """ - ) - provider_conf = hcl_res["provider"][0]["awscc"] - scan_result = check.scan_provider_conf(conf=provider_conf) - self.assertEqual(CheckResult.FAILED, scan_result) + passed_check_resources = set([c.resource for c in report.passed_checks]) + failed_check_resources = set([c.resource for c in report.failed_checks]) - def test_failure_access_key(self): - hcl_res = hcl2.loads( - """ - provider "awscc" { - region = "us-west-2" - access_key = "AKIAIOSFODNN7EXAMPLE" - } - """ - ) - provider_conf = hcl_res["provider"][0]["awscc"] - scan_result = check.scan_provider_conf(conf=provider_conf) - self.assertEqual(CheckResult.FAILED, scan_result) + self.assertEqual(summary["passed"], 1) + self.assertEqual(summary["failed"], 3) + self.assertEqual(summary["skipped"], 0) + self.assertEqual(summary["parsing_errors"], 0) - def test_failure_secret_key(self): - hcl_res = hcl2.loads( - """ - provider "awscc" { - region = "us-west-2" - secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" - } - """ - ) - provider_conf = hcl_res["provider"][0]["awscc"] - scan_result = check.scan_provider_conf(conf=provider_conf) - self.assertEqual(CheckResult.FAILED, scan_result) + self.assertEqual(passing_resources, passed_check_resources) + self.assertEqual(failing_resources, failed_check_resources) if __name__ == "__main__": From fc7511136e35608fc85dc716915582b85fe71b6e Mon Sep 17 00:00:00 2001 From: quixoticmonk Date: Thu, 6 Mar 2025 14:37:23 +0530 Subject: [PATCH 4/4] fix: use len than static values --- tests/terraform/checks/provider/awscc/test_credentials.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/terraform/checks/provider/awscc/test_credentials.py b/tests/terraform/checks/provider/awscc/test_credentials.py index d171a177a6..164ecebf0d 100644 --- a/tests/terraform/checks/provider/awscc/test_credentials.py +++ b/tests/terraform/checks/provider/awscc/test_credentials.py @@ -27,8 +27,8 @@ def test(self): passed_check_resources = set([c.resource for c in report.passed_checks]) failed_check_resources = set([c.resource for c in report.failed_checks]) - self.assertEqual(summary["passed"], 1) - self.assertEqual(summary["failed"], 3) + self.assertEqual(summary["passed"], len(passing_resources)) + self.assertEqual(summary["failed"], len(failing_resources)) self.assertEqual(summary["skipped"], 0) self.assertEqual(summary["parsing_errors"], 0)