fix(telegram): prevent silent wrong-bot routing when accountId not in config

When a non-default accountId is specified but not found in the accounts
config, resolveTelegramToken() falls through to channel-level defaults
(botToken, tokenFile, env) — silently routing messages via the wrong
bot's token. This is a cross-bot message leak with no error or warning.

Root cause: extensions/telegram/src/token.ts:44-46, resolveAccountCfg()
returns undefined for unknown accountIds but code continues to fallbacks.
Introduced in e5bca0832f when Telegram moved to extensions/.

Fix: return { token: "", source: "none" } with a diagnostic log when
a non-default accountId is not found. Existing behavior for known
accounts (with or without per-account tokens) preserved.

Test: added "does not fall through when non-default accountId not in
config" — 1/1 new, 10/10 existing unaffected.

Closes #49383

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HCL <chenglunhu@gmail.com>
This commit is contained in:
HCL
2026-03-20 13:13:01 +08:00
committed by Ayaan Zaidi
parent c64893a9c2
commit 4e45a663e7
2 changed files with 29 additions and 0 deletions

View File

@@ -188,6 +188,24 @@ describe("resolveTelegramToken", () => {
expect(res.source).toBe("none");
});
it("does not fall through to channel-level token when non-default accountId is not in config", () => {
vi.stubEnv("TELEGRAM_BOT_TOKEN", "");
const cfg = {
channels: {
telegram: {
botToken: "wrong-bot-token",
accounts: {
knownBot: { botToken: "known-bot-token" },
},
},
},
} as OpenClawConfig;
const res = resolveTelegramToken(cfg, { accountId: "unknownBot" });
expect(res.token).toBe("");
expect(res.source).toBe("none");
});
it("throws when botToken is an unresolved SecretRef object", () => {
const cfg = {
channels: {

View File

@@ -44,6 +44,17 @@ export function resolveTelegramToken(
const accountCfg = resolveAccountCfg(
accountId !== DEFAULT_ACCOUNT_ID ? accountId : DEFAULT_ACCOUNT_ID,
);
// When a non-default accountId is explicitly specified but not found in config,
// return empty immediately — do NOT fall through to channel-level defaults,
// which would silently route the message via the wrong bot's token.
if (accountId !== DEFAULT_ACCOUNT_ID && !accountCfg) {
opts.logMissingFile?.(
`channels.telegram.accounts: unknown accountId "${accountId}" — not found in config, refusing channel-level fallback`,
);
return { token: "", source: "none" };
}
const accountTokenFile = accountCfg?.tokenFile?.trim();
if (accountTokenFile) {
const token = tryReadSecretFileSync(