diff --git a/README.md b/README.md index af11ad99d..af00fee25 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ KeywordDetector MailchimpDetector NpmDetector PrivateKeyDetector +PypiTokenDetector SendGridDetector SlackDetector SoftlayerDetector diff --git a/detect_secrets/plugins/pypi_token.py b/detect_secrets/plugins/pypi_token.py new file mode 100644 index 000000000..5b0b2a5ed --- /dev/null +++ b/detect_secrets/plugins/pypi_token.py @@ -0,0 +1,20 @@ +""" +This plugin searches for PyPI tokens +""" +import re + +from detect_secrets.plugins.base import RegexBasedDetector + + +class PypiTokenDetector(RegexBasedDetector): + """Scans for PyPI tokens.""" + secret_type = 'PyPI Token' + + denylist = [ + # refs https://warehouse.pypa.io/development/token-scanning.html + # pypi.org token + re.compile(r'pypi-AgEIcHlwaS5vcmc[A-Za-z0-9-_]{70,}'), + + # test.pypi.org token + re.compile(r'pypi-AgENdGVzdC5weXBpLm9yZw[A-Za-z0-9-_]{70,}'), + ] diff --git a/tests/plugins/pypi_token_test.py b/tests/plugins/pypi_token_test.py new file mode 100644 index 000000000..5854b651c --- /dev/null +++ b/tests/plugins/pypi_token_test.py @@ -0,0 +1,29 @@ +import pytest + +from detect_secrets.plugins.pypi_token import PypiTokenDetector + + +class TestPypiTokenDetector: + + @pytest.mark.parametrize( + 'payload, should_flag', + [ + ( + # pragma: allowlist nextline secret + 'pypi-AgEIcHlwaS5vcmcCJDU3OTM1MjliLWIyYTYtNDEwOC05NzRkLTM0MjNiNmEwNWIzYgACF1sxLFsitesttestbWluaW1hbC1wcm9qZWN0Il1dAAIsWzIsWyJjYWY4OTAwZi0xNDMwLTRiYQstYmFmMi1mMDE3OGIyNWZhNTkiXV0AAAYgh2UINPjWBDwT0r3tQ1o5oZyswcjN0-IluP6z34SX3KM', True, # noqa: E501 + ), + ( + # pragma: allowlist nextline secret + 'pypi-AgENdGVzdC5weXBpLm9yZwIkN2YxOWZhOWEtY2FjYS00MGZhLTj2MGEtODFjMnE2MjdmMzY0AAIqWzMsImJlM2FiOWI5LTRmYUTnNEg4ZS04Mjk0LWFlY2Y2NWYzNGYzNyJdAAAGIMb5Hb8nVvhcAizcVVzA-bKKnwN7Pe0RmgPRCvrPwyJf', True, # noqa: E501 + ), + ( + # pragma: allowlist nextline secret + 'pypi-AgEIcHlwaS5vcmcCJDU3OTM1MjliLWIyYTYtNDEwOC05NzRkLTM0MjNiNmEwNWIzYgACF1sxLFsibWluaW1h', False, # noqa: E501 + ), + ], + ) + def test_analyze(self, payload, should_flag): + logic = PypiTokenDetector() + output = logic.analyze_line(filename='mock_filename', line=payload) + + assert len(output) == int(should_flag)