diff --git a/supabase/functions/configure-telegram-webhook/index.ts b/supabase/functions/configure-telegram-webhook/index.ts index 8848a8a..afe7d97 100644 --- a/supabase/functions/configure-telegram-webhook/index.ts +++ b/supabase/functions/configure-telegram-webhook/index.ts @@ -114,6 +114,7 @@ Deno.serve(async (req) => { headers: { "Content-Type": "application/json", Authorization: `Bearer ${serviceKey}`, + "X-Internal-Secret": Deno.env.get("INTERNAL_FUNCTION_SECRET") ?? "", }, body: JSON.stringify({ agent_instance_id: agent.id, diff --git a/supabase/functions/managed-bot-webhook/index.ts b/supabase/functions/managed-bot-webhook/index.ts index e08d082..9bdce4c 100644 --- a/supabase/functions/managed-bot-webhook/index.ts +++ b/supabase/functions/managed-bot-webhook/index.ts @@ -53,6 +53,20 @@ Deno.serve(async (req) => { return ack(); } + // Verificação de origem: se TELEGRAM_MANAGER_BOT_WEBHOOK_SECRET estiver + // configurado, exige o header X-Telegram-Bot-Api-Secret-Token correspondente. + // Caso contrário, opera em modo permissivo (comportamento anterior) + log. + const expectedSecret = Deno.env.get("TELEGRAM_MANAGER_BOT_WEBHOOK_SECRET") ?? ""; + if (expectedSecret) { + const incoming = req.headers.get("X-Telegram-Bot-Api-Secret-Token") ?? ""; + if (incoming !== expectedSecret) { + console.warn("managed-bot-webhook: invalid telegram secret token"); + return new Response("unauthorized", { status: 401 }); + } + } else { + console.warn("managed-bot-webhook: TELEGRAM_MANAGER_BOT_WEBHOOK_SECRET ausente — modo permissivo"); + } + if (!managerToken) { console.error("TELEGRAM_MANAGER_BOT_TOKEN ausente"); return ack(); @@ -165,6 +179,7 @@ Deno.serve(async (req) => { headers: { "Content-Type": "application/json", Authorization: `Bearer ${serviceKey}`, + "X-Internal-Secret": Deno.env.get("INTERNAL_FUNCTION_SECRET") ?? "", }, body: JSON.stringify({ agent_instance_id: agentInstance.id, diff --git a/supabase/functions/railway-webhook/index.ts b/supabase/functions/railway-webhook/index.ts index 4bd1af2..1cab649 100644 --- a/supabase/functions/railway-webhook/index.ts +++ b/supabase/functions/railway-webhook/index.ts @@ -48,9 +48,39 @@ Deno.serve(async (req) => { return jsonResponse(200, { ignored: true, reason: "method not allowed" }); } + const rawBody = await req.text(); + + // Verificação de assinatura: modo permissivo se RAILWAY_WEBHOOK_SECRET não estiver + // configurado (mantém comportamento atual). Quando configurado, exige HMAC SHA-256. + const railwaySecret = Deno.env.get("RAILWAY_WEBHOOK_SECRET") ?? ""; + if (railwaySecret) { + const sig = req.headers.get("X-Railway-Signature") ?? ""; + try { + const key = await crypto.subtle.importKey( + "raw", + new TextEncoder().encode(railwaySecret), + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"], + ); + const macBuf = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(rawBody)); + const macHex = Array.from(new Uint8Array(macBuf)).map((b) => b.toString(16).padStart(2, "0")).join(""); + const expected = sig.startsWith("sha256=") ? sig.slice(7) : sig; + if (expected !== macHex) { + console.warn("railway-webhook: invalid signature"); + return jsonResponse(401, { error: "invalid signature" }); + } + } catch (e) { + console.error("railway-webhook: signature verify failed:", String(e)); + return jsonResponse(401, { error: "signature verification failed" }); + } + } else { + console.warn("railway-webhook: RAILWAY_WEBHOOK_SECRET ausente — modo permissivo"); + } + let payload: Record; try { - payload = await req.json(); + payload = JSON.parse(rawBody); } catch { console.log("railway-webhook: invalid json body"); return jsonResponse(200, { ignored: true, reason: "invalid json" });