fix(security): gate slash/control commands
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import type { Bot, Context } from "grammy";
|
||||
|
||||
import { resolveEffectiveMessagesConfig } from "../agents/identity.js";
|
||||
import {
|
||||
@@ -16,6 +16,16 @@ import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/pr
|
||||
import { finalizeInboundContext } from "../auto-reply/reply/inbound-context.js";
|
||||
import { danger, logVerbose } from "../globals.js";
|
||||
import { resolveAgentRoute } from "../routing/resolve-route.js";
|
||||
import { resolveCommandAuthorizedFromAuthorizers } from "../channels/command-gating.js";
|
||||
import type { ChannelGroupPolicy } from "../config/group-policy.js";
|
||||
import type {
|
||||
ReplyToMode,
|
||||
TelegramAccountConfig,
|
||||
TelegramGroupConfig,
|
||||
TelegramTopicConfig,
|
||||
} from "../config/types.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { deliverReplies } from "./bot/delivery.js";
|
||||
import { buildInlineKeyboard } from "./send.js";
|
||||
import {
|
||||
@@ -27,6 +37,31 @@ import {
|
||||
import { firstDefined, isSenderAllowed, normalizeAllowFrom } from "./bot-access.js";
|
||||
import { readTelegramAllowFromStore } from "./pairing-store.js";
|
||||
|
||||
type TelegramNativeCommandContext = Context & { match?: string };
|
||||
|
||||
type RegisterTelegramNativeCommandsParams = {
|
||||
bot: Bot;
|
||||
cfg: ClawdbotConfig;
|
||||
runtime: RuntimeEnv;
|
||||
accountId: string;
|
||||
telegramCfg: TelegramAccountConfig;
|
||||
allowFrom?: Array<string | number>;
|
||||
groupAllowFrom?: Array<string | number>;
|
||||
replyToMode: ReplyToMode;
|
||||
textLimit: number;
|
||||
useAccessGroups: boolean;
|
||||
nativeEnabled: boolean;
|
||||
nativeSkillsEnabled: boolean;
|
||||
nativeDisabledExplicit: boolean;
|
||||
resolveGroupPolicy: (chatId: string | number) => ChannelGroupPolicy;
|
||||
resolveTelegramGroupConfig: (
|
||||
chatId: string | number,
|
||||
messageThreadId?: number,
|
||||
) => { groupConfig?: TelegramGroupConfig; topicConfig?: TelegramTopicConfig };
|
||||
shouldSkipUpdate: (ctx: unknown) => boolean;
|
||||
opts: { token: string };
|
||||
};
|
||||
|
||||
export const registerTelegramNativeCommands = ({
|
||||
bot,
|
||||
cfg,
|
||||
@@ -45,7 +80,7 @@ export const registerTelegramNativeCommands = ({
|
||||
resolveTelegramGroupConfig,
|
||||
shouldSkipUpdate,
|
||||
opts,
|
||||
}) => {
|
||||
}: RegisterTelegramNativeCommandsParams) => {
|
||||
const skillCommands =
|
||||
nativeEnabled && nativeSkillsEnabled ? listSkillCommandsForAgents({ cfg }) : [];
|
||||
const nativeCommands = nativeEnabled
|
||||
@@ -74,24 +109,15 @@ export const registerTelegramNativeCommands = ({
|
||||
];
|
||||
|
||||
if (allCommands.length > 0) {
|
||||
const api = bot.api as unknown as {
|
||||
setMyCommands?: (
|
||||
commands: Array<{ command: string; description: string }>,
|
||||
) => Promise<unknown>;
|
||||
};
|
||||
if (typeof api.setMyCommands === "function") {
|
||||
api.setMyCommands(allCommands).catch((err) => {
|
||||
bot.api.setMyCommands(allCommands).catch((err) => {
|
||||
runtime.error?.(danger(`telegram setMyCommands failed: ${String(err)}`));
|
||||
});
|
||||
} else {
|
||||
logVerbose("telegram: setMyCommands unavailable; skipping registration");
|
||||
}
|
||||
|
||||
if (typeof (bot as unknown as { command?: unknown }).command !== "function") {
|
||||
logVerbose("telegram: bot.command unavailable; skipping native handlers");
|
||||
} else {
|
||||
for (const command of nativeCommands) {
|
||||
bot.command(command.name, async (ctx) => {
|
||||
bot.command(command.name, async (ctx: TelegramNativeCommandContext) => {
|
||||
const msg = ctx.message;
|
||||
if (!msg) return;
|
||||
if (shouldSkipUpdate(ctx)) return;
|
||||
@@ -172,18 +198,17 @@ export const registerTelegramNativeCommands = ({
|
||||
: [];
|
||||
const senderId = msg.from?.id ? String(msg.from.id) : "";
|
||||
const senderUsername = msg.from?.username ?? "";
|
||||
const allowFromConfigured = allowFromList.length > 0;
|
||||
const commandAuthorized = allowFromConfigured
|
||||
? allowFromList.includes("*") ||
|
||||
(senderId && allowFromList.includes(senderId)) ||
|
||||
(senderId && allowFromList.includes(`telegram:${senderId}`)) ||
|
||||
(senderUsername &&
|
||||
allowFromList.some(
|
||||
(entry) =>
|
||||
entry.toLowerCase() === senderUsername.toLowerCase() ||
|
||||
entry.toLowerCase() === `@${senderUsername.toLowerCase()}`,
|
||||
))
|
||||
: !useAccessGroups;
|
||||
const dmAllow = normalizeAllowFrom(allowFromList);
|
||||
const senderAllowed = isSenderAllowed({
|
||||
allow: dmAllow,
|
||||
senderId,
|
||||
senderUsername,
|
||||
});
|
||||
const commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
|
||||
useAccessGroups,
|
||||
authorizers: [{ configured: dmAllow.hasEntries, allowed: senderAllowed }],
|
||||
modeWhenAccessGroupsOff: "configured",
|
||||
});
|
||||
if (!commandAuthorized) {
|
||||
await bot.api.sendMessage(chatId, "You are not authorized to use this command.");
|
||||
return;
|
||||
@@ -208,7 +233,7 @@ export const registerTelegramNativeCommands = ({
|
||||
cfg,
|
||||
})
|
||||
: null;
|
||||
if (menu) {
|
||||
if (menu && commandDefinition) {
|
||||
const title =
|
||||
menu.title ??
|
||||
`Choose ${menu.arg.description || menu.arg.name} for /${commandDefinition.nativeName ?? commandDefinition.key}.`;
|
||||
@@ -316,15 +341,8 @@ export const registerTelegramNativeCommands = ({
|
||||
}
|
||||
}
|
||||
} else if (nativeDisabledExplicit) {
|
||||
const api = bot.api as unknown as {
|
||||
setMyCommands?: (commands: []) => Promise<unknown>;
|
||||
};
|
||||
if (typeof api.setMyCommands === "function") {
|
||||
api.setMyCommands([]).catch((err) => {
|
||||
runtime.error?.(danger(`telegram clear commands failed: ${String(err)}`));
|
||||
});
|
||||
} else {
|
||||
logVerbose("telegram: setMyCommands unavailable; skipping clear");
|
||||
}
|
||||
bot.api.setMyCommands([]).catch((err) => {
|
||||
runtime.error?.(danger(`telegram clear commands failed: ${String(err)}`));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user