diff --git a/README.md b/README.md index 1a08f131c..56604cc85 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ CloudantDetector DiscordBotTokenDetector GitHubTokenDetector GitLabTokenDetector +HashiCorpVaultTokenDetector Base64HighEntropyString HexHighEntropyString IbmCloudIamDetector diff --git a/detect_secrets/plugins/hashicorp_vault_token.py b/detect_secrets/plugins/hashicorp_vault_token.py new file mode 100644 index 000000000..73218485d --- /dev/null +++ b/detect_secrets/plugins/hashicorp_vault_token.py @@ -0,0 +1,17 @@ +""" +This plugin searches for HashiCorp Vault tokens +""" +import re + +from detect_secrets.plugins.base import RegexBasedDetector + + +class HashiCorpVaultTokenDetector(RegexBasedDetector): + """Scans for HashiCorp Vault tokens.""" + secret_type = 'HashiCorp Vault Token' + + denylist = [ + # ref. https://github.blog/2021-04-05-behind-githubs-new-authentication-token-formats/ + # \b has been added to avoid many false positives when using Vault <=1.9 tokens + re.compile(r'(?:hv|\b)[brs]\.[A-Za-z0-9_-]{24,}'), + ] diff --git a/tests/plugins/hashicorp_vault_token_test.py b/tests/plugins/hashicorp_vault_token_test.py new file mode 100644 index 000000000..b4fa9fa68 --- /dev/null +++ b/tests/plugins/hashicorp_vault_token_test.py @@ -0,0 +1,23 @@ +import pytest + +from detect_secrets.plugins.hashicorp_vault_token import HashiCorpVaultTokenDetector + + +class TestHashiCorpVaultTokenDetector: + + @pytest.mark.parametrize( + 'payload, should_flag', + [ + ('hvs.wWPw5k4aXcaT4fNP0UcnZwJUVFk6LO0pINUx', True), + ('hvs.wWPw5k4aXcaT', False), + ('HideMyTokenhvs.wWPw5k4aXcaT4fNP0UcnZwJUVFk6LO0pINUx', True), + ('s.wWPw5k4aXcaT4fNP0UcnZwJUVFk6LO0pINUx', True), + ('MyClass.atLeast24CharactersField', False), + ('MyClas s.atLeast24CharactersField', True), + ('foo', False), + ], + ) + def test_analyze(self, payload, should_flag): + logic = HashiCorpVaultTokenDetector() + output = logic.analyze_line(filename='mock_filename', line=payload) + assert len(output) == int(should_flag)