fix(telegram): warn when setup leaves dmPolicy as pairing without allowFrom (#50710)

* fix(telegram): warn when setup leaves dmPolicy as pairing without allowFrom

* fix(telegram): scope setup warning to account config

* fix(telegram): quote setup allowFrom example

* fix: warn on insecure Telegram setup defaults (#50710) (thanks @ernestodeoliveira)

---------

Co-authored-by: Claude Code <claude-code@openclaw.ai>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
ernestodeoliveira 2026-03-20 00:59:33 -03:00 committed by GitHub
parent 991eb2ef03
commit 80110c550f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 130 additions and 1 deletions

View File

@ -78,6 +78,7 @@ Docs: https://docs.openclaw.ai
- Agents/compaction: extend the enclosing run deadline once while compaction is actively in flight, and abort the underlying SDK compaction on timeout/cancel so large-session compactions stop freezing mid-run. (#46889) Thanks @asyncjason.
- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026.
- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava.
- Telegram/setup: warn when setup leaves DMs on pairing without an allowlist, and show valid account-scoped remediation commands. (#50710) Thanks @ernestodeoliveira.
- Models/OpenRouter runtime capabilities: fetch uncatalogued OpenRouter model metadata on first use so newly added vision models keep image input instead of silently degrading to text-only, with top-level capability field fallbacks for `/api/v1/models`. (#45824) Thanks @DJjjjhao.
- Channels/plugins: keep shared interactive payloads merge-ready by fixing Slack custom callback routing and repeat-click dedupe, allowing interactive-only sends, and preserving ordered Discord shared text blocks. (#47715) Thanks @vincentkoc.
- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc.

View File

@ -0,0 +1,91 @@
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { telegramSetupWizard } from "./setup-surface.js";
async function runFinalize(cfg: OpenClawConfig, accountId: string) {
const prompter = {
note: vi.fn(async () => undefined),
};
await telegramSetupWizard.finalize?.({
cfg,
accountId,
credentialValues: {},
runtime: {} as never,
prompter: prompter as never,
forceAllowFrom: false,
});
return prompter.note;
}
describe("telegramSetupWizard.finalize", () => {
it("shows global config commands for the default account", async () => {
const note = await runFinalize(
{
channels: {
telegram: {
botToken: "tok",
},
},
},
DEFAULT_ACCOUNT_ID,
);
expect(note).toHaveBeenCalledWith(
expect.stringContaining('openclaw config set channels.telegram.dmPolicy "allowlist"'),
"Telegram DM access warning",
);
expect(note).toHaveBeenCalledWith(
expect.stringContaining(`openclaw config set channels.telegram.allowFrom '["YOUR_USER_ID"]'`),
"Telegram DM access warning",
);
});
it("shows account-scoped config commands for named accounts", async () => {
const note = await runFinalize(
{
channels: {
telegram: {
accounts: {
alerts: {
botToken: "tok",
},
},
},
},
},
"alerts",
);
expect(note).toHaveBeenCalledWith(
expect.stringContaining(
'openclaw config set channels.telegram.accounts.alerts.dmPolicy "allowlist"',
),
"Telegram DM access warning",
);
expect(note).toHaveBeenCalledWith(
expect.stringContaining(
`openclaw config set channels.telegram.accounts.alerts.allowFrom '["YOUR_USER_ID"]'`,
),
"Telegram DM access warning",
);
});
it("skips the warning when an allowFrom entry already exists", async () => {
const note = await runFinalize(
{
channels: {
telegram: {
botToken: "tok",
allowFrom: ["123"],
},
},
},
DEFAULT_ACCOUNT_ID,
);
expect(note).not.toHaveBeenCalled();
});
});

View File

@ -9,8 +9,13 @@ import {
splitSetupEntries,
} from "openclaw/plugin-sdk/setup";
import type { ChannelSetupDmPolicy, ChannelSetupWizard } from "openclaw/plugin-sdk/setup";
import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
import { inspectTelegramAccount } from "./account-inspect.js";
import { listTelegramAccountIds, resolveTelegramAccount } from "./accounts.js";
import {
listTelegramAccountIds,
mergeTelegramAccountConfig,
resolveTelegramAccount,
} from "./accounts.js";
import {
parseTelegramAllowFromId,
promptTelegramAllowFromForAccount,
@ -22,6 +27,29 @@ import {
const channel = "telegram" as const;
function shouldShowTelegramDmAccessWarning(cfg: OpenClawConfig, accountId: string): boolean {
const merged = mergeTelegramAccountConfig(cfg, accountId);
const policy = merged.dmPolicy ?? "pairing";
const hasAllowFrom =
Array.isArray(merged.allowFrom) && merged.allowFrom.some((e) => String(e).trim());
return policy === "pairing" && !hasAllowFrom;
}
function buildTelegramDmAccessWarningLines(accountId: string): string[] {
const configBase =
accountId === DEFAULT_ACCOUNT_ID
? "channels.telegram"
: `channels.telegram.accounts.${accountId}`;
return [
"Your bot is using DM policy: pairing.",
"Any Telegram user who discovers the bot can send pairing requests.",
"For private use, configure an allowlist with your Telegram user id:",
" " + formatCliCommand(`openclaw config set ${configBase}.dmPolicy "allowlist"`),
" " + formatCliCommand(`openclaw config set ${configBase}.allowFrom '["YOUR_USER_ID"]'`),
`Docs: ${formatDocsLink("/channels/pairing", "channels/pairing")}`,
];
}
const dmPolicy: ChannelSetupDmPolicy = {
label: "Telegram",
channel,
@ -104,6 +132,15 @@ export const telegramSetupWizard: ChannelSetupWizard = {
patch: { dmPolicy: "allowlist", allowFrom },
}),
}),
finalize: async ({ cfg, accountId, prompter }) => {
if (!shouldShowTelegramDmAccessWarning(cfg, accountId)) {
return;
}
await prompter.note(
buildTelegramDmAccessWarningLines(accountId).join("\n"),
"Telegram DM access warning",
);
},
dmPolicy,
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false),
};