fix(tgbot): require client ownership for non-admin link callbacks (#6489)

A non-admin tapping a subscription, individual-links or QR-links button was
served whatever email that button carried. Those keyboards outlive the chat
they were sent to (group chats, forwarded cards, a client whose tgId was
later revoked), so the email in the callback data cannot authorise itself.
The lookup the self-service usage command already performs now decides
whether the callback is served.

The gate also has to read the email at all: encodeQuery replaces any
callback payload past 64 chars with a hash, so for a client whose email is
long enough the non-admin path saw a bare hash and dropped the tap without a
word. The raw data is now decoded before the gate, which is the same decode
the admin path already performs, and an email the caller cannot prove is
answered with the generic error instead of silence.
This commit is contained in:
BlindMaster24
2026-09-13 12:46:14 +03:00
committed by GitHub
parent 72df05a403
commit 7ac5277c4f
3 changed files with 178 additions and 19 deletions
@@ -537,6 +537,21 @@ func (t *Tgbot) clientInfoMsg(
return output
}
// clientOwnedByTgUser reports whether email belongs to a client bound to this
// Telegram account, the same list the self-service usage command reads.
func (t *Tgbot) clientOwnedByTgUser(tgUserID int64, email string) bool {
traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserID)
if err != nil {
return false
}
for _, traffic := range traffics {
if traffic.Email == email {
return true
}
}
return false
}
// getClientUsage retrieves and sends client usage information to the chat.
func (t *Tgbot) getClientUsage(chatId int64, tgUserID int64, email ...string) {
traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserID)