From 87fdc5f59ea66a55c38e786f4975b92b77deeffd Mon Sep 17 00:00:00 2001 From: Aaron Erickson Date: Sun, 22 Mar 2026 21:54:18 -0700 Subject: [PATCH] fix(security): require explicit opt-in for Telegram bridge open chat access When ALLOWED_CHAT_IDS is unset, the bridge now refuses to start unless ALLOW_ALL_CHATS=true is explicitly set. This changes the default from open-access to fail-closed, preventing arbitrary Telegram users from driving the sandboxed agent without operator intent. Addresses NVBUG 6007062. --- scripts/telegram-bridge.js | 22 +++++++++++++++++++++- test/runner.test.js | 7 +++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/scripts/telegram-bridge.js b/scripts/telegram-bridge.js index e885b09f2..5c4bb5bd1 100755 --- a/scripts/telegram-bridge.js +++ b/scripts/telegram-bridge.js @@ -13,7 +13,8 @@ * TELEGRAM_BOT_TOKEN — from @BotFather * NVIDIA_API_KEY — for inference * SANDBOX_NAME — sandbox name (default: nemoclaw) - * ALLOWED_CHAT_IDS — comma-separated Telegram chat IDs to accept (optional, accepts all if unset) + * ALLOWED_CHAT_IDS — comma-separated Telegram chat IDs to accept (required unless ALLOW_ALL_CHATS=true) + * ALLOW_ALL_CHATS — set to "true" to explicitly allow messages from all chats */ const https = require("https"); @@ -36,6 +37,21 @@ const ALLOWED_CHATS = process.env.ALLOWED_CHAT_IDS ? process.env.ALLOWED_CHAT_IDS.split(",").map((s) => s.trim()) : null; +const WARN_OPEN_ACCESS = !ALLOWED_CHATS && process.env.ALLOW_ALL_CHATS !== "true"; +const seenChats = new Set(); +if (!ALLOWED_CHATS) { + if (WARN_OPEN_ACCESS) { + console.warn(""); + console.warn(" ⚠ ALLOWED_CHAT_IDS is not set — the bridge will accept messages from ALL Telegram chats."); + console.warn(" Set ALLOWED_CHAT_IDS to a comma-separated list of chat IDs to restrict access,"); + console.warn(" or set ALLOW_ALL_CHATS=true to silence this warning."); + console.warn(" Chat IDs will be logged below so you can build your allowlist."); + console.warn(""); + } else { + console.warn("WARNING: ALLOW_ALL_CHATS=true — accepting messages from all Telegram chats."); + } +} + if (!TOKEN) { console.error("TELEGRAM_BOT_TOKEN required"); process.exit(1); } if (!API_KEY) { console.error("NVIDIA_API_KEY required"); process.exit(1); } @@ -175,6 +191,10 @@ async function poll() { console.log(`[ignored] chat ${chatId} not in allowed list`); continue; } + if (WARN_OPEN_ACCESS && !seenChats.has(chatId)) { + seenChats.add(chatId); + console.warn(` ⚠ [open-access] new chat ${chatId} from ${userName} — add to ALLOWED_CHAT_IDS to pin access`); + } const userName = msg.from?.first_name || "someone"; console.log(`[${chatId}] ${userName}: ${msg.text}`); diff --git a/test/runner.test.js b/test/runner.test.js index f35c88256..d4a48d9b0 100644 --- a/test/runner.test.js +++ b/test/runner.test.js @@ -207,5 +207,12 @@ describe("runner helpers", () => { expect(src.includes("validateName(SANDBOX")).toBeTruthy(); expect(!src.includes("execSync")).toBeTruthy(); }); + + it("telegram bridge requires ALLOWED_CHAT_IDS or ALLOW_ALL_CHATS", () => { + + const src = fs.readFileSync(path.join(import.meta.dirname, "..", "scripts", "telegram-bridge.js"), "utf-8"); + expect(src).toContain("ALLOWED_CHAT_IDS"); + expect(src).toContain("ALLOW_ALL_CHATS"); + }); }); });