From 374b7728d457cab3047f8e12b34a8131abf3fee0 Mon Sep 17 00:00:00 2001 From: masami-agent Date: Fri, 1 May 2026 11:06:11 +0000 Subject: [PATCH 1/2] fix(gateway): constantize Telegram API URL + reject empty LINE source IDs #10: Extract TELEGRAM_API_BASE constant (matching LINE's LINE_API_BASE). Replaces 3 hardcoded URLs. Enables future mock testing. #11: Replace unwrap_or_default() with explicit empty-check + reject for LINE source IDs (groupId, roomId, userId). Per LINE API contract, these IDs are guaranteed present for their respective source types. Empty ID = anomalous payload, reject with warning log (fail-closed). Existing LINE dispatch tests unaffected (test reply/push routing, not source parsing). Partial fix for #676 --- gateway/src/adapters/line.rs | 26 +++++++++++++++++++++++--- gateway/src/adapters/telegram.rs | 10 +++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/gateway/src/adapters/line.rs b/gateway/src/adapters/line.rs index 95d56852..f4137b66 100644 --- a/gateway/src/adapters/line.rs +++ b/gateway/src/adapters/line.rs @@ -103,12 +103,32 @@ pub async fn webhook( let source = event.source.as_ref(); let (channel_id, channel_type) = match source { Some(s) if s.source_type == "group" => { - (s.group_id.clone().unwrap_or_default(), "group".to_string()) + match s.group_id.as_deref() { + Some(id) if !id.is_empty() => (id.to_string(), "group".to_string()), + _ => { + warn!("LINE group event missing groupId, skipping"); + continue; + } + } } Some(s) if s.source_type == "room" => { - (s.room_id.clone().unwrap_or_default(), "room".to_string()) + match s.room_id.as_deref() { + Some(id) if !id.is_empty() => (id.to_string(), "room".to_string()), + _ => { + warn!("LINE room event missing roomId, skipping"); + continue; + } + } + } + Some(s) => { + match s.user_id.as_deref() { + Some(id) if !id.is_empty() => (id.to_string(), "user".to_string()), + _ => { + warn!("LINE user event missing userId, skipping"); + continue; + } + } } - Some(s) => (s.user_id.clone().unwrap_or_default(), "user".to_string()), None => continue, }; let user_id = source diff --git a/gateway/src/adapters/telegram.rs b/gateway/src/adapters/telegram.rs index 5cbdc725..0ae9b55d 100644 --- a/gateway/src/adapters/telegram.rs +++ b/gateway/src/adapters/telegram.rs @@ -7,6 +7,10 @@ use std::sync::Arc; use tokio::sync::Mutex; use tracing::{error, info, warn}; +/// Base URL for Telegram Bot API. Extracted as constant for consistency +/// with LINE's `LINE_API_BASE` and to enable future mock testing. +pub const TELEGRAM_API_BASE: &str = "https://api.telegram.org"; + // --- Telegram types --- #[derive(Debug, Deserialize)] @@ -140,7 +144,7 @@ pub async fn handle_reply( if reply.command.as_deref() == Some("create_topic") { let req_id = reply.request_id.clone().unwrap_or_default(); info!(chat_id = %reply.channel.id, "creating forum topic"); - let url = format!("https://api.telegram.org/bot{bot_token}/createForumTopic"); + let url = format!("{TELEGRAM_API_BASE}/bot{bot_token}/createForumTopic"); let resp = client .post(&url) .json(&serde_json::json!({"chat_id": reply.channel.id, "name": reply.content.text})) @@ -222,7 +226,7 @@ pub async fn handle_reply( }) .unwrap_or_default() }; - let url = format!("https://api.telegram.org/bot{bot_token}/setMessageReaction"); + let url = format!("{TELEGRAM_API_BASE}/bot{bot_token}/setMessageReaction"); let _ = client .post(&url) .json(&serde_json::json!({ @@ -242,7 +246,7 @@ pub async fn handle_reply( thread_id = ?reply.channel.thread_id, "gateway → telegram" ); - let url = format!("https://api.telegram.org/bot{bot_token}/sendMessage"); + let url = format!("{TELEGRAM_API_BASE}/bot{bot_token}/sendMessage"); let _ = client .post(&url) .json(&serde_json::json!({ From d349b52abee5de2b8f3dda09a43837946cb2d778 Mon Sep 17 00:00:00 2001 From: masami-agent Date: Fri, 1 May 2026 11:24:18 +0000 Subject: [PATCH 2/2] fix: add warning log for LINE event missing source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consistency fix — all reject paths now have warning logs: - group missing groupId ✅ - room missing roomId ✅ - user missing userId ✅ - source missing (None) ✅ (this commit) --- gateway/src/adapters/line.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gateway/src/adapters/line.rs b/gateway/src/adapters/line.rs index f4137b66..3bfc36d0 100644 --- a/gateway/src/adapters/line.rs +++ b/gateway/src/adapters/line.rs @@ -129,7 +129,10 @@ pub async fn webhook( } } } - None => continue, + None => { + warn!("LINE event missing source, skipping"); + continue; + } }; let user_id = source .and_then(|s| s.user_id.as_deref())