From 112e06b5330647b6fce3a306e4da767170dd6164 Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Sat, 14 Mar 2026 17:03:10 +0100 Subject: [PATCH 1/5] feat(telegram_token): Add TelegramBotTokenDetector plugin Port the TelegramBotTokenDetector from Yelp/detect-secrets upstream. Includes anchored regex (^...$) to prevent false positives from substrings like AWS ARNs matching the bot token pattern. --- detect_secrets/plugins/telegram_token.py | 31 ++++++++++++++++++++++++ tests/plugins/telegram_token_test.py | 23 ++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 detect_secrets/plugins/telegram_token.py create mode 100644 tests/plugins/telegram_token_test.py diff --git a/detect_secrets/plugins/telegram_token.py b/detect_secrets/plugins/telegram_token.py new file mode 100644 index 000000000..01767a458 --- /dev/null +++ b/detect_secrets/plugins/telegram_token.py @@ -0,0 +1,31 @@ +""" +This plugin searches for Telegram bot tokens +""" +import re + +import requests + +from ..constants import VerifiedResult +from .base import RegexBasedDetector + + +class TelegramBotTokenDetector(RegexBasedDetector): + """Scans for Telegram bot tokens.""" + secret_type = 'Telegram Bot Token' + + denylist = [ + # refs https://core.telegram.org/bots/api#authorizing-your-bot + re.compile(r'^\d{8,10}:[0-9A-Za-z_-]{35}$'), + ] + + def verify(self, secret: str) -> VerifiedResult: # pragma: no cover + response = requests.get( + 'https://api.telegram.org/bot{}/getMe'.format( + secret, + ), + ) + return ( + VerifiedResult.VERIFIED_TRUE + if response.status_code == 200 + else VerifiedResult.VERIFIED_FALSE + ) diff --git a/tests/plugins/telegram_token_test.py b/tests/plugins/telegram_token_test.py new file mode 100644 index 000000000..a1df2d900 --- /dev/null +++ b/tests/plugins/telegram_token_test.py @@ -0,0 +1,23 @@ +import pytest + +from detect_secrets.plugins.telegram_token import TelegramBotTokenDetector + + +class TestTelegramTokenDetector: + + @pytest.mark.parametrize( + 'payload, should_flag', + [ + ('bot110201543:AAHdqTcvCH1vGWJxfSe1ofSAs0K5PALDsaw', False), + ('110201543:AAHdqTcvCH1vGWJxfSe1ofSAs0K5PALDsaw', True), + ('7213808860:AAH1bjqpKKW3maRSPAxzIU-0v6xNuq2-NjM', True), + ('foo:AAH1bjqpKKW3maRSPAxzIU-0v6xNuq2-NjM', False), + ('foo', False), + ('arn:aws:sns:aaa:111122223333:aaaaaaaaaaaaaaaaaaassssssddddddddddddd', False), + ], + ) + def test_analyze(self, payload, should_flag): + logic = TelegramBotTokenDetector() + output = logic.analyze_line(filename='mock_filename', line=payload) + + assert len(output) == int(should_flag) From 38be562191062b47a0edf28039f771196bfd47ed Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Sat, 14 Mar 2026 17:14:46 +0100 Subject: [PATCH 2/5] fix: address Copilot suggestions --- detect_secrets/plugins/telegram_token.py | 6 +++--- tests/plugins/telegram_token_test.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/detect_secrets/plugins/telegram_token.py b/detect_secrets/plugins/telegram_token.py index 01767a458..33efc9412 100644 --- a/detect_secrets/plugins/telegram_token.py +++ b/detect_secrets/plugins/telegram_token.py @@ -5,7 +5,7 @@ import requests -from ..constants import VerifiedResult +from detect_secrets.core.constants import VerifiedResult from .base import RegexBasedDetector @@ -18,10 +18,10 @@ class TelegramBotTokenDetector(RegexBasedDetector): re.compile(r'^\d{8,10}:[0-9A-Za-z_-]{35}$'), ] - def verify(self, secret: str) -> VerifiedResult: # pragma: no cover + def verify(self, token, *args, **kwargs): # pragma: no cover response = requests.get( 'https://api.telegram.org/bot{}/getMe'.format( - secret, + token, ), ) return ( diff --git a/tests/plugins/telegram_token_test.py b/tests/plugins/telegram_token_test.py index a1df2d900..6a589ae18 100644 --- a/tests/plugins/telegram_token_test.py +++ b/tests/plugins/telegram_token_test.py @@ -18,6 +18,6 @@ class TestTelegramTokenDetector: ) def test_analyze(self, payload, should_flag): logic = TelegramBotTokenDetector() - output = logic.analyze_line(filename='mock_filename', line=payload) + output = logic.analyze_line(payload, 1, 'mock_filename') assert len(output) == int(should_flag) From c73b54c04940b634dc74769c34ea5dcc11c31785 Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Sat, 14 Mar 2026 17:21:17 +0100 Subject: [PATCH 3/5] fix: address comment Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Pepe Fagoaga --- detect_secrets/plugins/telegram_token.py | 26 +++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/detect_secrets/plugins/telegram_token.py b/detect_secrets/plugins/telegram_token.py index 33efc9412..7803739d2 100644 --- a/detect_secrets/plugins/telegram_token.py +++ b/detect_secrets/plugins/telegram_token.py @@ -19,13 +19,19 @@ class TelegramBotTokenDetector(RegexBasedDetector): ] def verify(self, token, *args, **kwargs): # pragma: no cover - response = requests.get( - 'https://api.telegram.org/bot{}/getMe'.format( - token, - ), - ) - return ( - VerifiedResult.VERIFIED_TRUE - if response.status_code == 200 - else VerifiedResult.VERIFIED_FALSE - ) + try: + response = requests.get( + 'https://api.telegram.org/bot{}/getMe'.format( + token, + ), + timeout=5, + ) + except requests.exceptions.RequestException: + return VerifiedResult.UNVERIFIED + + if response.status_code == 200: + return VerifiedResult.VERIFIED_TRUE + + # For unexpected status codes (e.g., 429/5xx), avoid + # incorrectly marking the token as invalid. + return VerifiedResult.UNVERIFIED From fde1667b24515a805d20f5652bf8c45c47034f7f Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Sat, 14 Mar 2026 17:23:55 +0100 Subject: [PATCH 4/5] fix: enable plugin --- detect_secrets/core/usage.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/detect_secrets/core/usage.py b/detect_secrets/core/usage.py index 5d532f3d0..823f92724 100644 --- a/detect_secrets/core/usage.py +++ b/detect_secrets/core/usage.py @@ -618,6 +618,12 @@ class PluginOptions: help_text='Disables scans for GitHub credentials', filename='github_token', ), + PluginDescriptor( + classname='TelegramBotTokenDetector', + flag_text='--no-telegram-bot-token-scan', + help_text='Disables scans for Telegram bot tokens', + filename='telegram_token', + ), ] opt_in_plugins = [ PluginDescriptor( From 9741762f1774e16e58d045e9a5c1e9f6ae6e59fb Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Sat, 14 Mar 2026 17:38:03 +0100 Subject: [PATCH 5/5] fix: improve regex and verification --- detect_secrets/plugins/telegram_token.py | 6 +++--- tests/plugins/telegram_token_test.py | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/detect_secrets/plugins/telegram_token.py b/detect_secrets/plugins/telegram_token.py index 7803739d2..54cdc61cf 100644 --- a/detect_secrets/plugins/telegram_token.py +++ b/detect_secrets/plugins/telegram_token.py @@ -15,7 +15,7 @@ class TelegramBotTokenDetector(RegexBasedDetector): denylist = [ # refs https://core.telegram.org/bots/api#authorizing-your-bot - re.compile(r'^\d{8,10}:[0-9A-Za-z_-]{35}$'), + re.compile(r'(?