Skip to content

TelegramBotHandler: silent failure when API returns non-JSON response #2026

@ruttydm

Description

@ruttydm

Summary

TelegramBotHandler::sendCurl() silently swallows errors when the Telegram API (or an intermediary proxy) returns a non-JSON response. The handler returns as if the message was sent successfully, with no exception and no indication of failure.

Version: 3.10.0

Vulnerable code

TelegramBotHandler.php lines 277–285:

$result = Curl\Util::execute($ch);
if (!\is_string($result)) {
    throw new RuntimeException('Telegram API error. Description: No response');
}
$result = json_decode($result, true);

if ($result['ok'] === false) {
    throw new RuntimeException('Telegram API error. Description: ' . $result['description']);
}

Three layers of failure compound:

  1. json_decode() returns null for non-JSON input — no null check follows
  2. null['ok'] emits a warning and returns null in PHP 8+ — no type check
  3. null === false evaluates to false — the error check is bypassed entirely

Why it triggers

curl_exec() returns the response body for HTTP error status codes (502, 503, etc.), not false. The is_string() check on line 278 passes. The HTML/plaintext error page then proceeds to json_decode, which returns null.

Common real-world triggers:

  • Cloudflare 502 Bad Gateway (returns HTML)
  • Nginx error pages during Telegram API downtime
  • Truncated responses from network timeouts
  • Any proxy/CDN intercepting the request with a non-JSON error page

Impact

The handler's purpose is critical alerting. When it fails silently during infrastructure issues — exactly when alerts matter most — log messages are permanently lost with zero indication. The Logger's try/catch (Logger.php:389) only works if an exception is thrown; since this bug suppresses the exception, the failure is completely invisible.

For comparison: SlackWebhookHandler, SendGridHandler, and IFTTTHandler don't attempt response validation at all. TelegramBotHandler is the only handler that validates, but gets it wrong for the non-JSON case.

Suggested fix

$result = json_decode($result, true);

if (!is_array($result)) {
    throw new RuntimeException('Telegram API error. Description: Unexpected non-JSON response');
}

if ($result['ok'] === false) {
    throw new RuntimeException('Telegram API error. Description: ' . ($result['description'] ?? 'Unknown error'));
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions