From 2ace91c356354c340bd379afe3313c29abc7ec3d Mon Sep 17 00:00:00 2001 From: ChanochShayner <57212002+ChanochShayner@users.noreply.github.com> Date: Sun, 6 Aug 2023 13:01:34 +0300 Subject: [PATCH] feat(secrets): Make non-entropy signatures take precedence over entropy signatures (#5412) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding UT to fail before the implementation * add ut * remove log * fix condition * remove unrelated changes * Update tests/secrets/custom_and_entropy/main.tf Co-authored-by: Anton Grübel * revert * UT fix --------- Co-authored-by: Anton Grübel --- checkov/secrets/runner.py | 22 ++++++----- tests/secrets/custom_and_entropy/main.tf | 3 ++ tests/secrets/test_load_detectors.py | 47 ++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 tests/secrets/custom_and_entropy/main.tf diff --git a/checkov/secrets/runner.py b/checkov/secrets/runner.py index 1f3b5e54d96..f6dd4719ae8 100644 --- a/checkov/secrets/runner.py +++ b/checkov/secrets/runner.py @@ -72,6 +72,9 @@ 'Twilio API Key': 'CKV_SECRET_18', 'Hex High Entropy String': 'CKV_SECRET_19' } + +ENTROPY_CHECK_IDS = ('CKV_SECRET_6', 'CKV_SECRET_19', 'CKV_SECRET_80') + CHECK_ID_TO_SECRET_TYPE = {v: k for k, v in SECRET_TYPE_TO_ID.items()} @@ -209,8 +212,8 @@ def run( self.pbar.initiate(len(files_to_scan)) self._scan_files(files_to_scan, secrets, self.pbar) self.pbar.close() - secrets_duplication: dict[str, bool] = {} + secret_records: dict[str, SecretsRecord] = {} for key, secret in secrets: added_commit_hash, removed_commit_hash, code_line, added_by, removed_date, added_date = '', '', '', '', '', '' if runner_filter.enable_git_history_secret_scan: @@ -231,12 +234,11 @@ def run( logging.info( f"Removing secret due to UUID filtering: {hashlib.sha256(secret.secret_value.encode('utf-8')).hexdigest()}") continue - if secret_key in secrets_duplication: - logging.debug( - f'Secret was filtered - secrets_duplication. line_number {secret.line_number}, check_id {check_id}') - continue - else: - secrets_duplication[secret_key] = True + if secret_key in secret_records.keys(): + if secret_records[secret_key].check_id in ENTROPY_CHECK_IDS and check_id not in ENTROPY_CHECK_IDS: + secret_records.pop(secret_key) + else: + continue bc_check_id = metadata_integration.get_bc_id(check_id) if bc_check_id in secret_suppressions_id: logging.debug(f'Secret was filtered - check {check_id} was suppressed') @@ -275,7 +277,7 @@ def run( # via 'load_secret_from_dict' self.save_secret_to_coordinator(secret.secret_value, bc_check_id, resource, secret.line_number, result) line_text_censored = omit_secret_value_from_line(cast(str, secret.secret_value), line_text) - report.add_record(SecretsRecord( + secret_records[secret_key] = SecretsRecord( check_id=check_id, bc_check_id=bc_check_id, severity=severity, @@ -294,7 +296,9 @@ def run( added_by=added_by, removed_date=removed_date, added_date=added_date - )) + ) + for _, v in secret_records.items(): + report.add_record(v) enriched_secrets_s3_path = bc_integration.persist_enriched_secrets(self.secrets_coordinator.get_secrets()) if enriched_secrets_s3_path: diff --git a/tests/secrets/custom_and_entropy/main.tf b/tests/secrets/custom_and_entropy/main.tf new file mode 100644 index 00000000000..b97dc0c1a89 --- /dev/null +++ b/tests/secrets/custom_and_entropy/main.tf @@ -0,0 +1,3 @@ +resource "aws_instance" "a" { + test_pass = "z2b7k2cQfzc+yjP2K8cjuQ8uoorHBpEvC+XWhU3Z5+IdrPQYwr991Lj73xfZ+RA2GzC0wTedDTvb1C2NX+3Gpw==" +} \ No newline at end of file diff --git a/tests/secrets/test_load_detectors.py b/tests/secrets/test_load_detectors.py index 661de32c29b..81db4edc6f1 100644 --- a/tests/secrets/test_load_detectors.py +++ b/tests/secrets/test_load_detectors.py @@ -239,6 +239,53 @@ def test_custom_regex_detector(self): enable_secret_scan_all_files=True)) self.assertEqual(len(report.failed_checks), 3) + def test_non_entropy_take_precedence_over_entropy(self): + # given: File with entropy secret and custom secret + current_dir = os.path.dirname(os.path.realpath(__file__)) + valid_dir_path = current_dir + "/custom_and_entropy" + check_id = 'test1' + bc_integration.customer_run_config_response = {"secretsPolicies": [ + { + "incidentId": check_id, + "category": "Secrets", + "severity": "MEDIUM", + "incidentType": "Violation", + "title": check_id, + "guideline": "test", + "laceworkViolationId": None, + "prowlerCheckId": None, + "checkovCheckId": None, + "conditionQuery": { + "value": ['test_pass =\s*"(.*?)"'], + "cond_type": "secrets" + }, + "resourceTypes": + [ + "aws_instance" + ], + "provider": "AWS", + "remediationIds": + [], + "customerName": "test1", + "isCustom": True, + "code": None, + "descriptiveTitle": None, + "constructiveTitle": None, + "pcPolicyId": None, + "additionalPcPolicyIds": None, + "pcSeverity": None, + "sourceIncidentId": None + } + ]} + runner = Runner() + + # when: Running the secrets runner on the file + report = runner.run(root_folder=valid_dir_path, runner_filter=RunnerFilter(framework=['secrets'], enable_secret_scan_all_files=True)) + + # then: Validating that the non-entropy is the one. + self.assertEqual(len(report.failed_checks), 1) + self.assertEqual(report.failed_checks[0].check_id, check_id) + def test_custom_regex_detector_value_str(self): current_dir = os.path.dirname(os.path.realpath(__file__)) valid_dir_path = current_dir + "/custom_regex_detector"