mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 04:51:25 +00:00
fix(security): enforce trusted sender auth for discord moderation
This commit is contained in:
@@ -353,6 +353,47 @@ describe("handleDiscordMessageAction", () => {
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("uses trusted requesterSenderId for moderation and ignores params senderUserId", async () => {
|
||||
await handleDiscordMessageAction({
|
||||
action: "timeout",
|
||||
params: {
|
||||
guildId: "guild-1",
|
||||
userId: "user-2",
|
||||
durationMin: 5,
|
||||
senderUserId: "spoofed-admin-id",
|
||||
},
|
||||
cfg: {} as OpenClawConfig,
|
||||
requesterSenderId: "trusted-sender-id",
|
||||
toolContext: { currentChannelProvider: "discord" },
|
||||
});
|
||||
|
||||
expect(handleDiscordAction).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
action: "timeout",
|
||||
guildId: "guild-1",
|
||||
userId: "user-2",
|
||||
durationMinutes: 5,
|
||||
senderUserId: "trusted-sender-id",
|
||||
}),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects moderation when trusted sender id is missing in Discord tool context", async () => {
|
||||
await expect(
|
||||
handleDiscordMessageAction({
|
||||
action: "kick",
|
||||
params: {
|
||||
guildId: "guild-1",
|
||||
userId: "user-2",
|
||||
},
|
||||
cfg: {} as OpenClawConfig,
|
||||
toolContext: { currentChannelProvider: "discord" },
|
||||
}),
|
||||
).rejects.toThrow("Sender user ID required for Discord moderation actions.");
|
||||
expect(handleDiscordAction).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("telegramMessageActions", () => {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import type { ChannelMessageActionContext } from "../../types.js";
|
||||
import {
|
||||
readNumberParam,
|
||||
readStringArrayParam,
|
||||
readStringParam,
|
||||
} from "../../../../agents/tools/common.js";
|
||||
import { handleDiscordAction } from "../../../../agents/tools/discord-actions.js";
|
||||
import type { ChannelMessageActionContext } from "../../types.js";
|
||||
|
||||
type Ctx = Pick<ChannelMessageActionContext, "action" | "params" | "cfg" | "accountId">;
|
||||
type Ctx = Pick<
|
||||
ChannelMessageActionContext,
|
||||
"action" | "params" | "cfg" | "accountId" | "requesterSenderId" | "toolContext"
|
||||
>;
|
||||
|
||||
export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
ctx: Ctx;
|
||||
@@ -355,6 +358,11 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
const deleteMessageDays = readNumberParam(actionParams, "deleteDays", {
|
||||
integer: true,
|
||||
});
|
||||
const senderUserId = ctx.requesterSenderId?.trim() || undefined;
|
||||
// In channel/tool flows, require trusted sender identity for moderation authorization.
|
||||
if (ctx.toolContext?.currentChannelProvider === "discord" && !senderUserId) {
|
||||
throw new Error("Sender user ID required for Discord moderation actions.");
|
||||
}
|
||||
const discordAction = action;
|
||||
return await handleDiscordAction(
|
||||
{
|
||||
@@ -366,6 +374,7 @@ export async function tryHandleDiscordMessageActionGuildAdmin(params: {
|
||||
until,
|
||||
reason,
|
||||
deleteMessageDays,
|
||||
senderUserId,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import type { ChannelMessageActionContext } from "../../types.js";
|
||||
import {
|
||||
readNumberParam,
|
||||
readStringArrayParam,
|
||||
@@ -6,7 +7,6 @@ import {
|
||||
} from "../../../../agents/tools/common.js";
|
||||
import { handleDiscordAction } from "../../../../agents/tools/discord-actions.js";
|
||||
import { resolveDiscordChannelId } from "../../../../discord/targets.js";
|
||||
import type { ChannelMessageActionContext } from "../../types.js";
|
||||
import { tryHandleDiscordMessageActionGuildAdmin } from "./handle-action.guild-admin.js";
|
||||
|
||||
const providerId = "discord";
|
||||
@@ -22,7 +22,10 @@ function readParentIdParam(params: Record<string, unknown>): string | null | und
|
||||
}
|
||||
|
||||
export async function handleDiscordMessageAction(
|
||||
ctx: Pick<ChannelMessageActionContext, "action" | "params" | "cfg" | "accountId">,
|
||||
ctx: Pick<
|
||||
ChannelMessageActionContext,
|
||||
"action" | "params" | "cfg" | "accountId" | "requesterSenderId" | "toolContext"
|
||||
>,
|
||||
): Promise<AgentToolResult<unknown>> {
|
||||
const { action, params, cfg } = ctx;
|
||||
const accountId = ctx.accountId ?? readStringParam(params, "accountId");
|
||||
|
||||
@@ -304,6 +304,11 @@ export type ChannelMessageActionContext = {
|
||||
cfg: OpenClawConfig;
|
||||
params: Record<string, unknown>;
|
||||
accountId?: string | null;
|
||||
/**
|
||||
* Trusted sender id from inbound context. This is server-injected and must
|
||||
* never be sourced from tool/model-controlled params.
|
||||
*/
|
||||
requesterSenderId?: string | null;
|
||||
gateway?: {
|
||||
url?: string;
|
||||
token?: string;
|
||||
|
||||
Reference in New Issue
Block a user