From eee4649aff8727b28c900789809fdfe62d362b93 Mon Sep 17 00:00:00 2001 From: Pablo Santiago Date: Fri, 12 Mar 2021 13:49:50 +0100 Subject: [PATCH] Support for C++ programming language --- detect_secrets/plugins/keyword.py | 14 ++++++++++++++ detect_secrets/util/filetype.py | 4 +++- tests/plugins/keyword_test.py | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/detect_secrets/plugins/keyword.py b/detect_secrets/plugins/keyword.py index 8bebfdfa9..8ee51f51f 100644 --- a/detect_secrets/plugins/keyword.py +++ b/detect_secrets/plugins/keyword.py @@ -123,6 +123,7 @@ # e.g. my_password = "bar" # e.g. my_password = @"bar" # e.g. my_password[] = "bar"; + # e.g. char my_password[25] = "bar"; r'{denylist}({square_brackets})?{optional_whitespace}[!=]{{1,2}}{optional_whitespace}(@)?(")({secret})(\5)'.format( # noqa: E501 denylist=DENYLIST_REGEX, square_brackets=SQUARE_BRACKETS, @@ -131,6 +132,14 @@ ), flags=re.IGNORECASE, ) +FOLLOWEB_BY_OPTIONAL_ASSIGN_QUOTES_REQUIRED_REGEX = re.compile( + # e.g. std::string secret("bar"); + # e.g. secret.assign("bar",17); + r'{denylist}(.assign)?\((")({secret})(\3)'.format( + denylist=DENYLIST_REGEX, + secret=SECRET, + ), +) FOLLOWED_BY_EQUAL_SIGNS_REGEX = re.compile( # e.g. my_password = bar # e.g. my_password == "bar" or my_password != "bar" or my_password === "bar" @@ -199,6 +208,10 @@ COMMON_C_DENYLIST_REGEX_TO_GROUP = { FOLLOWED_BY_EQUAL_SIGNS_OPTIONAL_BRACKETS_OPTIONAL_AT_SIGN_QUOTES_REQUIRED_REGEX: 6, } +C_PLUS_PLUS_REGEX_TO_GROUP = { + FOLLOWEB_BY_OPTIONAL_ASSIGN_QUOTES_REQUIRED_REGEX: 4, + FOLLOWED_BY_EQUAL_SIGNS_QUOTES_REQUIRED_REGEX: 5, +} QUOTES_REQUIRED_DENYLIST_REGEX_TO_GROUP = { FOLLOWED_BY_COLON_QUOTES_REQUIRED_REGEX: 5, PRECEDED_BY_EQUAL_COMPARISON_SIGNS_QUOTES_REQUIRED_REGEX: 2, @@ -210,6 +223,7 @@ FileType.OBJECTIVE_C: COMMON_C_DENYLIST_REGEX_TO_GROUP, FileType.C_SHARP: COMMON_C_DENYLIST_REGEX_TO_GROUP, FileType.C: COMMON_C_DENYLIST_REGEX_TO_GROUP, + FileType.C_PLUS_PLUS: C_PLUS_PLUS_REGEX_TO_GROUP, FileType.CLS: QUOTES_REQUIRED_DENYLIST_REGEX_TO_GROUP, FileType.JAVA: QUOTES_REQUIRED_DENYLIST_REGEX_TO_GROUP, FileType.JAVASCRIPT: QUOTES_REQUIRED_DENYLIST_REGEX_TO_GROUP, diff --git a/detect_secrets/util/filetype.py b/detect_secrets/util/filetype.py index 5ee507634..bd3887d9d 100644 --- a/detect_secrets/util/filetype.py +++ b/detect_secrets/util/filetype.py @@ -16,7 +16,8 @@ class FileType(Enum): YAML = 10 C_SHARP = 11 C = 12 - OTHER = 13 + C_PLUS_PLUS = 13 + OTHER = 14 def determine_file_type(filename: str) -> FileType: @@ -38,4 +39,5 @@ def determine_file_type(filename: str) -> FileType: '.yml': FileType.YAML, '.cs': FileType.C_SHARP, '.c': FileType.C, + '.cpp': FileType.C_PLUS_PLUS }.get(file_extension, FileType.OTHER) diff --git a/tests/plugins/keyword_test.py b/tests/plugins/keyword_test.py index 38e252982..54c20edd2 100644 --- a/tests/plugins/keyword_test.py +++ b/tests/plugins/keyword_test.py @@ -81,6 +81,8 @@ COMMON_C_TEST_CASES = [ ('apikey = "{}";'.format(COMMON_SECRET), COMMON_SECRET), + ('if (secret == "{}")'.format(COMMON_SECRET), COMMON_SECRET), # Comparison + ('if (db_pass != "{}")'.format(COMMON_SECRET), COMMON_SECRET), # Comparison ('password = @"{}";'.format(COMMON_SECRET), COMMON_SECRET), ('my_password_secure = @"{}";'.format(COMMON_SECRET), COMMON_SECRET), # Prefix/suffix ('secrete[] = "{}";'.format(COMMON_SECRET), COMMON_SECRET), @@ -99,6 +101,20 @@ ('some_key = "real_secret"', None), # We cannot make 'key' a Keyword, too noisy) ] +C_PLUS_PLUS_TEST_CASES = [ + ('apikey = "{}";'.format(COMMON_SECRET), COMMON_SECRET), + ('my_password_secure = "{}";'.format(COMMON_SECRET), COMMON_SECRET), # Prefix and suffix + ('password = {}'.format(COMMON_SECRET), None), # Secret without quotes + ('if (secret == "{}")'.format(COMMON_SECRET), COMMON_SECRET), # Comparison + ('if (db_pass != "{}")'.format(COMMON_SECRET), COMMON_SECRET), # Comparison + ('std::string secret("{}");'.format(COMMON_SECRET), COMMON_SECRET), + ('secrete.assign("{}",17);'.format(COMMON_SECRET), COMMON_SECRET), + ('api_key = ""', None), # Nothing in the quotes + ('password = "somefakekey"', None), # 'fake' in the secret + ('password = ${link}', None), # Has a ${ followed by a } + ('some_key = "real_secret"', None), # We cannot make 'key' a Keyword, too noisy) +] + QUOTES_REQUIRED_TEST_CASES = [ ('apikey: "{}"'.format(COMMON_SECRET), COMMON_SECRET), ('apikey_myservice: "{}"'.format(COMMON_SECRET), COMMON_SECRET), # Suffix