mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-07 22:09:57 +00:00
fix(discord): gate component command authorization for guild interactions (#26119)
* Discord: gate component command authorization * test: cover allowlisted guild component authorization path (#26119) (thanks @bmendonca3) --------- Co-authored-by: Brian Mendonca <brianmendonca@Brians-MacBook-Air.local> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -23,6 +23,7 @@ import { formatInboundEnvelope, resolveEnvelopeFormatOptions } from "../../auto-
|
||||
import { finalizeInboundContext } from "../../auto-reply/reply/inbound-context.js";
|
||||
import { dispatchReplyWithBufferedBlockDispatcher } from "../../auto-reply/reply/provider-dispatcher.js";
|
||||
import { createReplyReferencePlanner } from "../../auto-reply/reply/reply-reference.js";
|
||||
import { resolveCommandAuthorizedFromAuthorizers } from "../../channels/command-gating.js";
|
||||
import { createReplyPrefixOptions } from "../../channels/reply-prefix.js";
|
||||
import { recordInboundSession } from "../../channels/session.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
@@ -727,6 +728,57 @@ function formatModalSubmissionText(
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function resolveComponentCommandAuthorized(params: {
|
||||
ctx: AgentComponentContext;
|
||||
interactionCtx: ComponentInteractionContext;
|
||||
channelConfig: ReturnType<typeof resolveDiscordChannelConfigWithFallback>;
|
||||
guildInfo: ReturnType<typeof resolveDiscordGuildEntry>;
|
||||
allowNameMatching: boolean;
|
||||
}): boolean {
|
||||
const { ctx, interactionCtx, channelConfig, guildInfo } = params;
|
||||
if (interactionCtx.isDirectMessage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ownerAllowList = normalizeDiscordAllowList(ctx.allowFrom, ["discord:", "user:", "pk:"]);
|
||||
const ownerOk = ownerAllowList
|
||||
? resolveDiscordAllowListMatch({
|
||||
allowList: ownerAllowList,
|
||||
candidate: {
|
||||
id: interactionCtx.user.id,
|
||||
name: interactionCtx.user.username,
|
||||
tag: formatDiscordUserTag(interactionCtx.user),
|
||||
},
|
||||
allowNameMatching: params.allowNameMatching,
|
||||
}).allowed
|
||||
: false;
|
||||
|
||||
const { hasAccessRestrictions, memberAllowed } = resolveDiscordMemberAccessState({
|
||||
channelConfig,
|
||||
guildInfo,
|
||||
memberRoleIds: interactionCtx.memberRoleIds,
|
||||
sender: {
|
||||
id: interactionCtx.user.id,
|
||||
name: interactionCtx.user.username,
|
||||
tag: formatDiscordUserTag(interactionCtx.user),
|
||||
},
|
||||
allowNameMatching: params.allowNameMatching,
|
||||
});
|
||||
const useAccessGroups = ctx.cfg.commands?.useAccessGroups !== false;
|
||||
const authorizers = useAccessGroups
|
||||
? [
|
||||
{ configured: ownerAllowList != null, allowed: ownerOk },
|
||||
{ configured: hasAccessRestrictions, allowed: memberAllowed },
|
||||
]
|
||||
: [{ configured: hasAccessRestrictions, allowed: memberAllowed }];
|
||||
|
||||
return resolveCommandAuthorizedFromAuthorizers({
|
||||
useAccessGroups,
|
||||
authorizers,
|
||||
modeWhenAccessGroupsOff: "configured",
|
||||
});
|
||||
}
|
||||
|
||||
async function dispatchDiscordComponentEvent(params: {
|
||||
ctx: AgentComponentContext;
|
||||
interaction: AgentComponentInteraction;
|
||||
@@ -780,12 +832,20 @@ async function dispatchDiscordComponentEvent(params: {
|
||||
parentSlug: channelCtx.parentSlug,
|
||||
scope: channelCtx.isThread ? "thread" : "channel",
|
||||
});
|
||||
const allowNameMatching = isDangerousNameMatchingEnabled(ctx.discordConfig);
|
||||
const groupSystemPrompt = channelConfig?.systemPrompt?.trim() || undefined;
|
||||
const ownerAllowFrom = resolveDiscordOwnerAllowFrom({
|
||||
channelConfig,
|
||||
guildInfo,
|
||||
sender: { id: interactionCtx.user.id, name: interactionCtx.user.username, tag: senderTag },
|
||||
allowNameMatching: isDangerousNameMatchingEnabled(ctx.discordConfig),
|
||||
allowNameMatching,
|
||||
});
|
||||
const commandAuthorized = resolveComponentCommandAuthorized({
|
||||
ctx,
|
||||
interactionCtx,
|
||||
channelConfig,
|
||||
guildInfo,
|
||||
allowNameMatching,
|
||||
});
|
||||
const storePath = resolveStorePath(ctx.cfg.session?.store, { agentId });
|
||||
const envelopeOptions = resolveEnvelopeFormatOptions(ctx.cfg);
|
||||
@@ -830,7 +890,7 @@ async function dispatchDiscordComponentEvent(params: {
|
||||
Provider: "discord" as const,
|
||||
Surface: "discord" as const,
|
||||
WasMentioned: true,
|
||||
CommandAuthorized: true,
|
||||
CommandAuthorized: commandAuthorized,
|
||||
CommandSource: "text" as const,
|
||||
MessageSid: interaction.rawData.id,
|
||||
Timestamp: timestamp,
|
||||
|
||||
Reference in New Issue
Block a user