diff --git a/internal/server/telegram_bot.go b/internal/server/telegram_bot.go index 294fb2f..21a74de 100644 --- a/internal/server/telegram_bot.go +++ b/internal/server/telegram_bot.go @@ -2328,6 +2328,11 @@ func (bot *telegramBot) loadConfig(ctx context.Context) (telegramRuntimeConfig, } func (bot *telegramBot) call(ctx context.Context, config telegramRuntimeConfig, method string, payload any, result any) error { + // Telegram polling is a long-lived notification channel and must use the + // same administrator-configured destination exceptions as test messages, + // SMS pushes and automatic-task notifications. This keeps SSRF protection + // enabled while allowing explicit DNS Fake-IP ranges such as 198.18/15. + ctx = bot.notificationDestinationContext(ctx) base, err := validateTelegramAPIURL(ctx, config.BaseURL, config.Token, method) if err != nil { return redactTelegramError(err, config.Token) @@ -2370,6 +2375,13 @@ func (bot *telegramBot) call(ctx context.Context, config telegramRuntimeConfig, return nil } +func (bot *telegramBot) notificationDestinationContext(ctx context.Context) context.Context { + if bot.server == nil { + return ctx + } + return bot.server.notificationDestinationContext(ctx) +} + func (bot *telegramBot) sendText(ctx context.Context, config telegramRuntimeConfig, chatID int64, text string, replyMarkup any) error { target := config.ChatID if chatID != 0 { diff --git a/internal/server/telegram_bot_test.go b/internal/server/telegram_bot_test.go index 3076d6c..d6126af 100644 --- a/internal/server/telegram_bot_test.go +++ b/internal/server/telegram_bot_test.go @@ -3,6 +3,7 @@ package server import ( "context" "errors" + "net/netip" "strings" "testing" "time" @@ -57,6 +58,19 @@ func TestTelegramAPIURLRejectsMalformedTemplates(t *testing.T) { } } +func TestTelegramPollingUsesExplicitFakeIPDestinationAllowlist(t *testing.T) { + bot := &telegramBot{server: &Server{access: parsedAccessConfig{ + cidrs: []netip.Prefix{netip.MustParsePrefix("198.18.0.0/15")}, + }}} + ctx := bot.notificationDestinationContext(context.Background()) + if _, err := validateTelegramAPIURL(ctx, "https://198.18.0.34", "123456:test-token", "getUpdates"); err != nil { + t.Fatalf("explicitly allowed Telegram Fake-IP was rejected: %v", err) + } + if _, err := validateTelegramAPIURL(ctx, "https://169.254.169.254", "123456:test-token", "getUpdates"); err == nil { + t.Fatal("metadata address became reachable through Telegram allowlist") + } +} + func TestParseTelegramCommand(t *testing.T) { command, remainder := parseTelegramCommand(" /sms@vocat_bot EC20 +447700900123 hello world ") if command != "sms" || remainder != "EC20 +447700900123 hello world" {