From bd735482f7791249231dfaf14e1d817fc2cb6ec3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 21 Apr 2026 21:17:34 +0800 Subject: [PATCH] =?UTF-8?q?fix(telegram):=20BUTTON=5FDATA=5FINVALID=20?= =?UTF-8?q?=E2=80=94=20nonce=20=E8=B6=85=E9=81=8E=2064=20bytes=20=E6=A0=B9?= =?UTF-8?q?=E5=9B=A0=E4=BF=AE=E5=BE=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:Telegram callback_data 上限 64 bytes。 5 個長 action 名(docker_restart/host_restart_service 等)+ UUID approval_id = 71-77 bytes → BUTTON_DATA_INVALID。 修復: 1. security_interceptor.generate_callback_nonce:若 nonce > 63 bytes, 改用 3-part 格式(捨棄 random)— timestamp 仍保時間唯一性。 2. security_interceptor.parse_callback_data:接受 3-part 或 4-part 格式。 3. telegram_gateway:移除 debug payload logging(診斷完成)。 影響 action:docker_restart / host_restart_service / host_clear_log / reload_nginx / renew_cert(全部 > 7 chars + UUID = 64 bytes 以上)。 Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/services/security_interceptor.py | 10 +++++++++- apps/api/src/services/telegram_gateway.py | 8 -------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/api/src/services/security_interceptor.py b/apps/api/src/services/security_interceptor.py index 6946807d..2b7e1414 100644 --- a/apps/api/src/services/security_interceptor.py +++ b/apps/api/src/services/security_interceptor.py @@ -411,10 +411,17 @@ class TelegramSecurityInterceptor: nonce = f"{action}:{approval_id}:{timestamp}:{random_part}" + # Telegram callback_data limit is 64 bytes. + # Long action names (e.g. docker_restart=14 chars) with UUID approval_id push nonce to 71+ bytes. + # Drop the random suffix when over limit — timestamp still guarantees temporal uniqueness. + if len(nonce.encode()) > 63: + nonce = f"{action}:{approval_id}:{timestamp}" + logger.debug( "callback_nonce_generated", approval_id=approval_id, action=action, + nonce_len=len(nonce.encode()), ) return nonce @@ -447,7 +454,8 @@ class TelegramSecurityInterceptor: "is_info_action": True, } - if len(parts) != 4: + # 接受 3-part(長 action 名截斷 random)和 4-part(標準)兩種格式 + if len(parts) not in (3, 4): raise ValueError(f"Invalid callback_data format: {callback_data}") return { diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index 0fb4db77..ad3910b6 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -1378,14 +1378,6 @@ class TelegramGateway: ) as span: try: response = await self._http_client.post(url, json=payload) - if response.status_code >= 400: - import json as _json - _payload_str = _json.dumps(payload, ensure_ascii=False) - logger.error("telegram_api_rejected", method=method, - status=response.status_code, - response_body=response.text[:300], - payload_len=len(_payload_str), - payload_preview=_payload_str[:1000]) response.raise_for_status() result = response.json()