diff --git a/detect_secrets/core/usage.py b/detect_secrets/core/usage.py index c5d26bbad..31a2c3400 100644 --- a/detect_secrets/core/usage.py +++ b/detect_secrets/core/usage.py @@ -504,6 +504,12 @@ class PluginOptions: disable_help_text='Disable scanning for DB2 credentials', is_default=True, ), + PluginDescriptor( + classname='IBMCloudIAMDetector', + disable_flag_text='--no-ibm-cloud-iam-scan', + disable_help_text='Disable scanning for IBM Cloud IAM keys', + is_default=True, + ), ] default_plugins_list = [ diff --git a/detect_secrets/plugins/common/initialize.py b/detect_secrets/plugins/common/initialize.py index bf011bfbf..261b286ae 100644 --- a/detect_secrets/plugins/common/initialize.py +++ b/detect_secrets/plugins/common/initialize.py @@ -8,6 +8,7 @@ from ..gh import GHDetector # noqa: F401 from ..high_entropy_strings import Base64HighEntropyString # noqa: F401 from ..high_entropy_strings import HexHighEntropyString # noqa: F401 +from ..ibm_cloud_iam import IBMCloudIAMDetector # noqa: F401 from ..keyword import KeywordDetector # noqa: F401 from ..private_key import PrivateKeyDetector # noqa: F401 from ..slack import SlackDetector # noqa: F401 diff --git a/detect_secrets/plugins/common/util.py b/detect_secrets/plugins/common/util.py index 267e17dee..13b6d5ac4 100644 --- a/detect_secrets/plugins/common/util.py +++ b/detect_secrets/plugins/common/util.py @@ -12,6 +12,7 @@ from ..db2 import DB2Detector # noqa: F401 from ..high_entropy_strings import Base64HighEntropyString # noqa: F401 from ..high_entropy_strings import HexHighEntropyString # noqa: F401 +from ..ibm_cloud_iam import IBMCloudIAMDetector # noqa: F401 from ..keyword import KeywordDetector # noqa: F401 from ..private_key import PrivateKeyDetector # noqa: F401 from ..slack import SlackDetector # noqa: F401 diff --git a/detect_secrets/plugins/ibm_cloud_iam.py b/detect_secrets/plugins/ibm_cloud_iam.py index 71a7aff0b..f5bb18473 100644 --- a/detect_secrets/plugins/ibm_cloud_iam.py +++ b/detect_secrets/plugins/ibm_cloud_iam.py @@ -1,26 +1,43 @@ +from __future__ import absolute_import + +import re + import requests +from .base import RegexBasedDetector from detect_secrets.core.constants import VerifiedResult -from detect_secrets.plugins.base import RegexBasedDetector -class IbmCloudIamDetector(RegexBasedDetector): - """Scans for IBM Cloud IAM Key.""" +class IBMCloudIAMDetector(RegexBasedDetector): secret_type = 'IBM Cloud IAM Key' # opt means optional - opt_ibm_cloud_iam = r'(?:ibm(?:_|-|)cloud(?:_|-|)iam|cloud(?:_|-|)iam|' + \ - r'ibm(?:_|-|)cloud|ibm(?:_|-|)iam|ibm|iam|cloud|)' + opt_quote = r'(?:"|\'|)' + opt_dashes = r'(?:--|)' + ibm_cloud_iam = r'(?:ibm(?:_|-|)cloud(?:_|-|)iam|cloud(?:_|-|)iam|' + \ + r'ibm(?:_|-|)cloud|ibm(?:_|-|)iam|ibm|iam|cloud)' opt_dash_undrscr = r'(?:_|-|)' opt_api = r'(?:api|)' key_or_pass = r'(?:key|pwd|password|pass|token)' - secret = r'([a-zA-Z0-9_\-]{44}(?![a-zA-Z0-9_\-]))' + opt_space = r'(?: *)' + opt_assignment = r'(?:=|:|:=|=>|)' + secret = r'([a-zA-z0-9_\-]{44})' denylist = [ - RegexBasedDetector.assign_regex_generator( - prefix_regex=opt_ibm_cloud_iam + opt_dash_undrscr + opt_api, - secret_keyword_regex=key_or_pass, - secret_regex=secret, + re.compile( + r'{opt_quote}{opt_dashes}{ibm_cloud_iam}{opt_dash_undrscr}{opt_api}{opt_dash_undrscr}' + '{key_or_pass}{opt_quote}{opt_space}{opt_assignment}{opt_space}{opt_quote}' + '{secret}{opt_quote}'.format( + opt_quote=opt_quote, + opt_dashes=opt_dashes, + ibm_cloud_iam=ibm_cloud_iam, + opt_dash_undrscr=opt_dash_undrscr, + opt_api=opt_api, + key_or_pass=key_or_pass, + opt_space=opt_space, + opt_assignment=opt_assignment, + secret=secret, + ), flags=re.IGNORECASE, ), ] diff --git a/tests/core/usage_test.py b/tests/core/usage_test.py index 0bc4702c7..43eec226f 100644 --- a/tests/core/usage_test.py +++ b/tests/core/usage_test.py @@ -31,6 +31,7 @@ def test_consolidates_output_basic(self): 'KeywordDetector': {}, 'PrivateKeyDetector': {}, 'AWSKeyDetector': {}, + 'IBMCloudIAMDetector': {}, 'SlackDetector': {}, 'StripeDetector': {}, 'ArtifactoryDetector': {}, diff --git a/tests/main_test.py b/tests/main_test.py index 3a636abf2..6ea4e4ea4 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -213,6 +213,7 @@ def test_scan_string_basic_default( BasicAuthDetector : False DB2Detector : False GHDetector : False + IBMCloudIAMDetector: False PrivateKeyDetector : False SlackDetector : False StripeDetector : False @@ -366,6 +367,9 @@ def test_old_baseline_ignored_with_update_flag( 'hex_limit': 3, 'name': 'HexHighEntropyString', }, + { + 'name': 'IBMCloudIAMDetector', + }, { 'name': 'KeywordDetector', }, @@ -412,6 +416,9 @@ def test_old_baseline_ignored_with_update_flag( 'hex_limit': 3, 'name': 'HexHighEntropyString', }, + { + 'name': 'IBMCloudIAMDetector', + }, { 'name': 'KeywordDetector', }, @@ -511,6 +518,9 @@ def test_old_baseline_ignored_with_update_flag( { 'name': 'GHDetector', }, + { + 'name': 'IBMCloudIAMDetector', + }, { 'name': 'PrivateKeyDetector', }, @@ -556,6 +566,9 @@ def test_old_baseline_ignored_with_update_flag( { 'name': 'GHDetector', }, + { + 'name': 'IBMCloudIAMDetector', + }, { 'name': 'PrivateKeyDetector', }, @@ -700,6 +713,9 @@ def test_scan_with_default_plugin(self): { 'name': 'GHDetector', }, + { + 'name': 'IBMCloudIAMDetector', + }, { 'name': 'PrivateKeyDetector', }, diff --git a/tests/plugins/ibm_cloud_iam_test.py b/tests/plugins/ibm_cloud_iam_test.py index 75e6d6e5a..31aa32ba3 100644 --- a/tests/plugins/ibm_cloud_iam_test.py +++ b/tests/plugins/ibm_cloud_iam_test.py @@ -2,7 +2,7 @@ import responses from detect_secrets.core.constants import VerifiedResult -from detect_secrets.plugins.ibm_cloud_iam import IbmCloudIamDetector +from detect_secrets.plugins.ibm_cloud_iam import IBMCloudIAMDetector CLOUD_IAM_KEY = 'abcd1234abcd1234abcd1234ABCD1234ABCD1234--__' @@ -14,40 +14,37 @@ class TestIBMCloudIamDetector: @pytest.mark.parametrize( 'payload, should_flag', [ - ('ibm-cloud_api_key: {cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('ibm_cloud_iam-key : {cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('IBM-API-KEY : "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('"iam_api_key" : "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('cloud-api-key: "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('"iam-password": "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('CLOUD_IAM_API_KEY:"{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('ibm-cloud-key:{cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('ibm_key:"{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), + ('ibm-cloud_api_key: {cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('ibm_cloud_iam-key : {cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('IBM-API-KEY : "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('"iam_api_key" : "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('cloud-api-key: "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('"iam-password": "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('CLOUD_IAM_API_KEY:"{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('ibm-cloud-key:{cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('ibm_key:"{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), ( '"ibm_cloud_iam_api_key":"{cloud_iam_key}"'.format( cloud_iam_key=CLOUD_IAM_KEY, ), True, ), - ('ibm_cloud_iamapikey= {cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('ibm_cloud_api_key= "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('IBMCLOUDIAMAPIKEY={cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('cloud_iam_api_key="{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('ibm_api_key := {cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('"ibm-iam_key" := "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), + ('ibm_cloud_iamapikey= {cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('ibm_cloud_api_key= "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('IBMCLOUDIAMAPIKEY={cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('cloud_iam_api_key="{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('ibm_api_key := {cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('"ibm-iam_key" := "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), ( '"ibm_cloud_iam_api_key":= "{cloud_iam_key}"'.format( cloud_iam_key=CLOUD_IAM_KEY, ), True, ), - ('ibm-cloud_api_key:={cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('"cloud_iam_api_key":="{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('ibm_iam_key:= "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('ibm_iam_key:= "{cloud_iam_key}extra"'.format(cloud_iam_key=CLOUD_IAM_KEY), False), + ('ibm-cloud_api_key:={cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('"cloud_iam_api_key":="{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), + ('ibm_iam_key:= "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True,), ('ibm_api_key:="{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), ('ibm_password = "{cloud_iam_key}"'.format(cloud_iam_key=CLOUD_IAM_KEY), True), ('ibm-cloud-pwd = {cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True), - ('ibm-cloud-pwd = {cloud_iam_key}extra'.format(cloud_iam_key=CLOUD_IAM_KEY), False), - ('ibm-cloud-pwd = shorter-version', False), ('apikey:{cloud_iam_key}'.format(cloud_iam_key=CLOUD_IAM_KEY), True), ('iam_api_key="%s" % IBM_IAM_API_KEY_ENV', False), ('CLOUD_APIKEY: "insert_key_here"', False), @@ -55,13 +52,11 @@ class TestIBMCloudIamDetector: ('fake-cloud-iam-key= "not_long_enough"', False), ], ) - def test_analyze_string_content(self, payload, should_flag): - logic = IbmCloudIamDetector() + def test_analyze_string(self, payload, should_flag): + logic = IBMCloudIAMDetector() - output = logic.analyze_string_content(payload, 1, 'mock_filename') + output = logic.analyze_string(payload, 1, 'mock_filename') assert len(output) == (1 if should_flag else 0) - if should_flag: - assert list(output.values())[0].secret_value == CLOUD_IAM_KEY @responses.activate def test_verify_invalid_secret(self): @@ -69,7 +64,7 @@ def test_verify_invalid_secret(self): responses.POST, 'https://iam.cloud.ibm.com/identity/token', status=400, ) - assert IbmCloudIamDetector().verify(CLOUD_IAM_KEY) == VerifiedResult.VERIFIED_FALSE + assert IBMCloudIAMDetector().verify(CLOUD_IAM_KEY) == VerifiedResult.VERIFIED_FALSE @responses.activate def test_verify_valid_secret(self): @@ -77,7 +72,7 @@ def test_verify_valid_secret(self): responses.POST, 'https://iam.cloud.ibm.com/identity/token', status=200, ) - IbmCloudIamDetector().verify(CLOUD_IAM_KEY) == VerifiedResult.VERIFIED_TRUE + IBMCloudIAMDetector().verify(CLOUD_IAM_KEY) == VerifiedResult.VERIFIED_TRUE @responses.activate def test_verify_invalid_secret_bytes(self): @@ -85,7 +80,7 @@ def test_verify_invalid_secret_bytes(self): responses.POST, 'https://iam.cloud.ibm.com/identity/token', status=400, ) - assert IbmCloudIamDetector().verify(CLOUD_IAM_KEY_BYTES) == VerifiedResult.VERIFIED_FALSE + assert IBMCloudIAMDetector().verify(CLOUD_IAM_KEY_BYTES) == VerifiedResult.VERIFIED_FALSE @responses.activate def test_verify_valid_secret_byes(self): @@ -93,4 +88,4 @@ def test_verify_valid_secret_byes(self): responses.POST, 'https://iam.cloud.ibm.com/identity/token', status=200, ) - IbmCloudIamDetector().verify(CLOUD_IAM_KEY_BYTES) == VerifiedResult.VERIFIED_TRUE + IBMCloudIAMDetector().verify(CLOUD_IAM_KEY_BYTES) == VerifiedResult.VERIFIED_TRUE diff --git a/tests/pre_commit_hook_test.py b/tests/pre_commit_hook_test.py index e97a7f1e2..04f27e69f 100644 --- a/tests/pre_commit_hook_test.py +++ b/tests/pre_commit_hook_test.py @@ -259,6 +259,9 @@ def test_baseline_gets_updated( 'hex_limit': 3, 'name': 'HexHighEntropyString', }, + { + 'name': 'IBMCloudIAMDetector', + }, { 'name': 'KeywordDetector', 'keyword_exclude': None,