From 17422608b244f7278cff90e2fcc107c5733b6cdf Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 5 Jan 2026 02:03:02 +0100 Subject: [PATCH] fix: gate /activation to owners in groups --- src/auto-reply/reply/commands.ts | 18 +++++++++- src/gateway/server.misc.test.ts | 59 ++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/auto-reply/reply/commands.ts b/src/auto-reply/reply/commands.ts index dbe9d2367..0c38e0cf2 100644 --- a/src/auto-reply/reply/commands.ts +++ b/src/auto-reply/reply/commands.ts @@ -165,7 +165,23 @@ export async function handleCommands(params: { reply: { text: "⚙️ Group activation only applies to group chats." }, }; } - if (!command.isAuthorizedSender) { + const activationOwnerList = + command.ownerList.length > 0 + ? command.ownerList + : command.isWhatsAppSurface && command.to + ? [normalizeE164(command.to)] + : []; + const activationSenderE164 = command.senderE164 + ? normalizeE164(command.senderE164) + : ""; + const isActivationOwner = + Boolean(activationSenderE164) && + activationOwnerList.includes(activationSenderE164); + + if ( + !command.isAuthorizedSender || + (command.isWhatsAppSurface && !isActivationOwner) + ) { logVerbose( `Ignoring /activation from unauthorized sender in group: ${command.senderE164 || ""}`, ); diff --git a/src/gateway/server.misc.test.ts b/src/gateway/server.misc.test.ts index 6ae7fd6b3..6c4f88cab 100644 --- a/src/gateway/server.misc.test.ts +++ b/src/gateway/server.misc.test.ts @@ -17,35 +17,42 @@ import { installGatewayTestHooks(); describe("gateway server misc", () => { - test("hello-ok advertises the gateway port for canvas host", async () => { - const prevToken = process.env.CLAWDBOT_GATEWAY_TOKEN; - process.env.CLAWDBOT_GATEWAY_TOKEN = "secret"; - testTailnetIPv4.value = "100.64.0.1"; - testState.gatewayBind = "lan"; - const canvasPort = await getFreePort(); - testState.canvasHostPort = canvasPort; + test( + "hello-ok advertises the gateway port for canvas host", + { timeout: 15_000 }, + async () => { + const prevToken = process.env.CLAWDBOT_GATEWAY_TOKEN; + process.env.CLAWDBOT_GATEWAY_TOKEN = "secret"; + testTailnetIPv4.value = "100.64.0.1"; + testState.gatewayBind = "lan"; + const canvasPort = await getFreePort(); + testState.canvasHostPort = canvasPort; - const port = await getFreePort(); - const server = await startGatewayServer(port, { - bind: "lan", - allowCanvasHostInTests: true, - }); - const ws = new WebSocket(`ws://127.0.0.1:${port}`, { - headers: { Host: `100.64.0.1:${port}` }, - }); - await new Promise((resolve) => ws.once("open", resolve)); + const port = await getFreePort(); + const server = await startGatewayServer(port, { + bind: "lan", + allowCanvasHostInTests: true, + }); + const ws = new WebSocket(`ws://127.0.0.1:${port}`, { + headers: { Host: `100.64.0.1:${port}` }, + }); + await new Promise((resolve, reject) => { + ws.once("open", () => resolve()); + ws.once("error", (err) => reject(err)); + }); - const hello = await connectOk(ws, { token: "secret" }); - expect(hello.canvasHostUrl).toBe(`http://100.64.0.1:${canvasPort}`); + const hello = await connectOk(ws, { token: "secret" }); + expect(hello.canvasHostUrl).toBe(`http://100.64.0.1:${canvasPort}`); - ws.close(); - await server.close(); - if (prevToken === undefined) { - delete process.env.CLAWDBOT_GATEWAY_TOKEN; - } else { - process.env.CLAWDBOT_GATEWAY_TOKEN = prevToken; - } - }); + ws.close(); + await server.close(); + if (prevToken === undefined) { + delete process.env.CLAWDBOT_GATEWAY_TOKEN; + } else { + process.env.CLAWDBOT_GATEWAY_TOKEN = prevToken; + } + }, + ); test("send dedupes by idempotencyKey", { timeout: 8000 }, async () => { const { server, ws } = await startServerWithClient();