fix: keep telegram plugin callbacks explicit

This commit is contained in:
Ayaan Zaidi
2026-03-29 10:56:36 +05:30
parent 5d81b64343
commit 7a16a48198
2 changed files with 72 additions and 0 deletions

View File

@@ -78,6 +78,7 @@ import {
} from "./conversation-route.js";
import { enforceTelegramDmAccess } from "./dm-access.js";
import {
isTelegramExecApprovalApprover,
isTelegramExecApprovalAuthorizedSender,
shouldEnableTelegramExecApprovalButtons,
} from "./exec-approvals.js";
@@ -98,6 +99,18 @@ import {
} from "./model-buttons.js";
import { buildInlineKeyboard } from "./send.js";
function parseApprovalCallbackId(data: string): string | null {
const trimmed = data.trim();
if (!trimmed.startsWith("/approve")) {
return null;
}
const tokens = trimmed.split(/\s+/).filter(Boolean);
if (tokens.length < 3) {
return null;
}
return tokens[1] ?? null;
}
export const registerTelegramHandlers = ({
cfg,
accountId,
@@ -1288,12 +1301,23 @@ export const registerTelegramHandlers = ({
const runtimeCfg = telegramDeps.loadConfig();
if (isApprovalCallback) {
const approvalId = parseApprovalCallbackId(data);
const isPluginApprovalCallback = approvalId?.startsWith("plugin:") ?? false;
if (!isTelegramExecApprovalAuthorizedSender({ cfg: runtimeCfg, accountId, senderId })) {
logVerbose(
`Blocked telegram exec approval callback from ${senderId || "unknown"} (not an approver)`,
);
return;
}
if (
isPluginApprovalCallback &&
!isTelegramExecApprovalApprover({ cfg: runtimeCfg, accountId, senderId })
) {
logVerbose(
`Blocked telegram plugin approval callback from ${senderId || "unknown"} (not an explicit approver)`,
);
return;
}
try {
await clearCallbackButtons();
} catch (editErr) {

View File

@@ -512,6 +512,54 @@ describe("createTelegramBot", () => {
expect(answerCallbackQuerySpy).toHaveBeenCalledWith("cbq-approve-blocked");
});
it("keeps plugin approval callback buttons for target-only recipients", async () => {
onSpy.mockClear();
editMessageReplyMarkupSpy.mockClear();
editMessageTextSpy.mockClear();
loadConfig.mockReturnValue({
approvals: {
exec: {
enabled: true,
mode: "targets",
targets: [{ channel: "telegram", to: "9" }],
},
},
channels: {
telegram: {
dmPolicy: "open",
allowFrom: ["*"],
capabilities: ["vision"],
},
},
});
createTelegramBot({ token: "tok" });
const callbackHandler = onSpy.mock.calls.find((call) => call[0] === "callback_query")?.[1] as (
ctx: Record<string, unknown>,
) => Promise<void>;
expect(callbackHandler).toBeDefined();
await callbackHandler({
callbackQuery: {
id: "cbq-plugin-approve-blocked",
data: "/approve plugin:138e9b8c allow-once",
from: { id: 9, first_name: "Ada", username: "ada_bot" },
message: {
chat: { id: 1234, type: "private" },
date: 1736380800,
message_id: 24,
text: "Plugin approval required.",
},
},
me: { username: "openclaw_bot" },
getFile: async () => ({ download: async () => new Uint8Array() }),
});
expect(editMessageReplyMarkupSpy).not.toHaveBeenCalled();
expect(editMessageTextSpy).not.toHaveBeenCalled();
expect(answerCallbackQuerySpy).toHaveBeenCalledWith("cbq-plugin-approve-blocked");
});
it("edits commands list for pagination callbacks", async () => {
onSpy.mockClear();
listSkillCommandsForAgents.mockClear();