diff --git a/src/discord/monitor/agent-components.ts b/src/discord/monitor/agent-components.ts index 898b0d419dc..1db4c77e64b 100644 --- a/src/discord/monitor/agent-components.ts +++ b/src/discord/monitor/agent-components.ts @@ -205,6 +205,40 @@ async function ensureGuildComponentMemberAllowed(params: { return false; } +async function ensureAgentComponentInteractionAllowed(params: { + ctx: AgentComponentContext; + interaction: AgentComponentInteraction; + channelId: string; + rawGuildId: string | undefined; + memberRoleIds: string[]; + user: DiscordUser; + replyOpts: { ephemeral?: boolean }; + componentLabel: string; + unauthorizedReply: string; +}): Promise<{ parentId: string | undefined } | null> { + const guildInfo = resolveDiscordGuildEntry({ + guild: params.interaction.guild ?? undefined, + guildEntries: params.ctx.guildEntries, + }); + const channelCtx = resolveDiscordChannelContext(params.interaction); + const memberAllowed = await ensureGuildComponentMemberAllowed({ + interaction: params.interaction, + guildInfo, + channelId: params.channelId, + rawGuildId: params.rawGuildId, + channelCtx, + memberRoleIds: params.memberRoleIds, + user: params.user, + replyOpts: params.replyOpts, + componentLabel: params.componentLabel, + unauthorizedReply: params.unauthorizedReply, + }); + if (!memberAllowed) { + return null; + } + return { parentId: channelCtx.parentId }; +} + export type AgentComponentContext = { cfg: OpenClawConfig; accountId: string; @@ -430,29 +464,23 @@ export class AgentComponentButton extends Button { memberRoleIds, } = interactionCtx; - // P2 FIX: Check user allowlist before processing component interaction - // This prevents unauthorized users from injecting system events - const guildInfo = resolveDiscordGuildEntry({ - guild: interaction.guild ?? undefined, - guildEntries: this.ctx.guildEntries, - }); - const channelCtx = resolveDiscordChannelContext(interaction); - const { parentId } = channelCtx; - const memberAllowed = await ensureGuildComponentMemberAllowed({ + // Check user allowlist before processing component interaction + // This prevents unauthorized users from injecting system events. + const allowed = await ensureAgentComponentInteractionAllowed({ + ctx: this.ctx, interaction, - guildInfo, channelId, rawGuildId, - channelCtx, memberRoleIds, user, replyOpts, componentLabel: "button", unauthorizedReply: "You are not authorized to use this button.", }); - if (!memberAllowed) { + if (!allowed) { return; } + const { parentId } = allowed; // Resolve route with full context (guildId, proper peer kind, parentPeer) const route = resolveAgentRoute({ @@ -537,28 +565,22 @@ export class AgentSelectMenu extends StringSelectMenu { memberRoleIds, } = interactionCtx; - // Check user allowlist before processing component interaction - const guildInfo = resolveDiscordGuildEntry({ - guild: interaction.guild ?? undefined, - guildEntries: this.ctx.guildEntries, - }); - const channelCtx = resolveDiscordChannelContext(interaction); - const { parentId } = channelCtx; - const memberAllowed = await ensureGuildComponentMemberAllowed({ + // Check user allowlist before processing component interaction. + const allowed = await ensureAgentComponentInteractionAllowed({ + ctx: this.ctx, interaction, - guildInfo, channelId, rawGuildId, - channelCtx, memberRoleIds, user, replyOpts, componentLabel: "select", unauthorizedReply: "You are not authorized to use this select menu.", }); - if (!memberAllowed) { + if (!allowed) { return; } + const { parentId } = allowed; // Extract selected values const values = interaction.values ?? [];