fix: tolerate malformed telegram callbacks, constrain channel id
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
This commit is contained in:
@@ -67,21 +67,31 @@ class TelegramWebhookController extends Controller
|
|||||||
$this->telegram->touch($chatId);
|
$this->telegram->touch($chatId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$callbackId = (string) ($callback['id'] ?? '');
|
||||||
[$action, $token] = array_pad(explode(':', (string) ($callback['data'] ?? ''), 2), 2, '');
|
[$action, $token] = array_pad(explode(':', (string) ($callback['data'] ?? ''), 2), 2, '');
|
||||||
$delivery = Delivery::where('action_token', $token)->with('event.user')->first();
|
$delivery = Delivery::where('action_token', $token)->with('event.user')->first();
|
||||||
|
|
||||||
if ($delivery === null || $delivery->event->isTerminal() || ! in_array($action, ['ack', 'done'], true)) {
|
if ($delivery === null || $delivery->event->isTerminal() || ! in_array($action, ['ack', 'done'], true)) {
|
||||||
$this->telegram->answerCallback((string) $callback['id'], 'Уже неактуально');
|
$this->answerCallback($callbackId, 'Уже неактуально');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($action === 'done') {
|
if ($action === 'done') {
|
||||||
$this->actions->done($delivery->event);
|
$this->actions->done($delivery->event);
|
||||||
$this->telegram->answerCallback((string) $callback['id'], 'Закрыто');
|
$this->answerCallback($callbackId, 'Закрыто');
|
||||||
} else {
|
} else {
|
||||||
$this->actions->ack($delivery->event);
|
$this->actions->ack($delivery->event);
|
||||||
$this->telegram->answerCallback((string) $callback['id'], 'Ок, напомню позже');
|
$this->answerCallback($callbackId, 'Ок, напомню позже');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function answerCallback(string $callbackId, string $text): void
|
||||||
|
{
|
||||||
|
if ($callbackId === '') {
|
||||||
|
// Нет id — отвечать нечему (malformed payload), но не считаем это ошибкой.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->telegram->answerCallback($callbackId, $text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ Route::middleware('remote.user')->group(function () {
|
|||||||
Route::post('/me/events/{event}/done', [MeEvents::class, 'done']);
|
Route::post('/me/events/{event}/done', [MeEvents::class, 'done']);
|
||||||
Route::get('/me/channels', [ChannelsController::class, 'index']);
|
Route::get('/me/channels', [ChannelsController::class, 'index']);
|
||||||
Route::post('/me/channels', [ChannelsController::class, 'store']);
|
Route::post('/me/channels', [ChannelsController::class, 'store']);
|
||||||
Route::delete('/me/channels/{channel}', [ChannelsController::class, 'destroy']);
|
Route::delete('/me/channels/{channel}', [ChannelsController::class, 'destroy'])->whereNumber('channel');
|
||||||
Route::post('/me/channels/telegram/link', [ChannelsController::class, 'telegramLink']);
|
Route::post('/me/channels/telegram/link', [ChannelsController::class, 'telegramLink']);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ class ChannelsApiTest extends TestCase
|
|||||||
$this->getJson('/me', ['X-Remote-User' => 'other']);
|
$this->getJson('/me', ['X-Remote-User' => 'other']);
|
||||||
$foreign = Channel::create(['user_id' => User::where('login', 'other')->sole()->id, 'type' => ChannelType::Webhook, 'config' => ['deliver_url' => 'u', 'presence_url' => 'p'], 'enabled' => true]);
|
$foreign = Channel::create(['user_id' => User::where('login', 'other')->sole()->id, 'type' => ChannelType::Webhook, 'config' => ['deliver_url' => 'u', 'presence_url' => 'p'], 'enabled' => true]);
|
||||||
$this->deleteJson('/me/channels/'.$foreign->id, [], $this->as)->assertNotFound();
|
$this->deleteJson('/me/channels/'.$foreign->id, [], $this->as)->assertNotFound();
|
||||||
|
|
||||||
|
$this->deleteJson('/me/channels/abc', [], $this->as)->assertNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_telegram_link_code(): void
|
public function test_telegram_link_code(): void
|
||||||
|
|||||||
@@ -96,4 +96,12 @@ class TelegramWebhookTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->postJson('/hooks/telegram', ['edited_message' => ['chat' => ['id' => 1]]], $this->secret())->assertNoContent();
|
$this->postJson('/hooks/telegram', ['edited_message' => ['chat' => ['id' => 1]]], $this->secret())->assertNoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_malformed_callback_is_ignored(): void
|
||||||
|
{
|
||||||
|
$this->postJson('/hooks/telegram', ['callback_query' => ['data' => 'ack:nope']], $this->secret())->assertNoContent();
|
||||||
|
|
||||||
|
$this->postJson('/hooks/telegram', ['callback_query' => ['id' => 'x', 'data' => 'garbage']], $this->secret())->assertNoContent();
|
||||||
|
Http::assertSent(fn (Request $r) => str_ends_with($r->url(), '/answerCallbackQuery') && $r['text'] === 'Уже неактуально');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user