Repository: bridgecrewio/checkov

Issue:
False positive for CKV_AWS_46: "Ensure no hard coded AWS access key and secret key exists in EC2 user data" 1
The [`string_has_secrets`](https://github.com/bridgecrewio/checkov/blob/master/checkov/common/util/secrets.py) function returns `True` when passed any SHA1 hash. This is problematic because with Terraform resources similar to:

```
resource "aws_instance" "test" {
   // ...
   user_data = filebase64("provision.sh")
}
```

The plan is generated using the hash of the user data, e.g.:

```
10:28:13  #16 29.02       + user_data                    = "d1b0706c4879b31a195d01f3a967e8b35d95cbb7"
```

... which `string_has_secrets` considers as a secret. My suggest would be to have the function return `False` if the user_data is _only_ a SHA1 or MD5 hash


Discussion:

Resolving patch:
diff --git a/checkov/common/util/secrets.py b/checkov/common/util/secrets.py
--- a/checkov/common/util/secrets.py
+++ b/checkov/common/util/secrets.py
@@ -54,6 +54,16 @@
 # now combine all the compiled patterns into one long list
 _patterns['all'] = list(itertools.chain.from_iterable(_patterns.values()))
 
+_hash_patterns = list(map(lambda regex: re.compile(regex, re.IGNORECASE), ['^[a-f0-9]{32}$', '^[a-f0-9]{40}$']))
+def is_hash(s: str) -> bool:
+    """
+    Checks whether a string is a MD5 or SHA1 hash
+
+    :param s:
+    :return:
+    """
+    return any([pattern.search(s) for pattern in _hash_patterns])
+
 
 def string_has_secrets(s: str, *categories) -> bool:
     """
@@ -76,6 +86,9 @@ def string_has_secrets(s: str, *categories) -> bool:
     if not categories or 'all' in categories:
         categories = ['all']
 
+    if is_hash(s):
+        return False
+
     for c in categories:
         if any([pattern.search(s) for pattern in _patterns[c]]):
             return True


Test patch:
diff --git a/tests/terraform/runner/test_plan_runner.py b/tests/terraform/runner/test_plan_runner.py
--- a/tests/terraform/runner/test_plan_runner.py
+++ b/tests/terraform/runner/test_plan_runner.py
@@ -117,8 +117,8 @@ def test_runner_root_dir(self):
         self.assertEqual(report.get_exit_code(soft_fail=False), 1)
         self.assertEqual(report.get_exit_code(soft_fail=True), 0)
 
-        self.assertEqual(61, report.get_summary()["failed"])
-        self.assertEqual(60, report.get_summary()["passed"])
+        self.assertEqual(60, report.get_summary()["failed"])
+        self.assertEqual(61, report.get_summary()["passed"])
 
         files_scanned = list(set(map(lambda rec: rec.file_path, report.failed_checks)))
         self.assertGreaterEqual(5, len(files_scanned))
diff --git a/tests/unit/test_secrets.py b/tests/unit/test_secrets.py
--- a/tests/unit/test_secrets.py
+++ b/tests/unit/test_secrets.py
@@ -28,3 +28,11 @@ def test_secrets(self):
 
         # check explicit all plus another category
         self.assertEqual(3, sum(1 for s in test_strings if string_has_secrets(s, ALL, AWS)))
+
+    # Regression test for https://github.com/bridgecrewio/checkov/issues/754
+    def test_does_not_consider_single_hash_as_a_secret(self):
+        # SHA1
+        self.assertFalse(string_has_secrets("b5a5b36b6be8d98c6f1bea655536d67abef23be8"))
+
+        # MD5
+        self.assertFalse(string_has_secrets("d9de48cf0676e9edb99bd8ee1ed44a21"))