Discord: gate audio preflight on member access (#57695)

* Discord: gate audio preflight on member access

* Discord: trim unauthorized sender logging

* CI: retrigger after review follow-up

* Discord: document blocked-sender log privacy
This commit is contained in:
Jacob Tomlinson
2026-03-30 06:38:22 -07:00
committed by GitHub
parent a77928b108
commit ee52f64226
2 changed files with 75 additions and 15 deletions

View File

@@ -765,6 +765,66 @@ describe("preflightDiscordMessage", () => {
expect(result?.wasMentioned).toBe(true);
});
it("does not transcribe guild audio from unauthorized members", async () => {
const channelId = "channel-audio-unauthorized-1";
const guildId = "guild-audio-unauthorized-1";
const client = createGuildTextClient(channelId);
const message = createDiscordMessage({
id: "m-audio-unauthorized-1",
channelId,
content: "",
attachments: [
{
id: "att-1",
url: "https://cdn.discordapp.com/attachments/voice.ogg",
content_type: "audio/ogg",
filename: "voice.ogg",
},
],
author: {
id: "user-2",
bot: false,
username: "Mallory",
},
});
const result = await preflightDiscordMessage({
...createPreflightArgs({
cfg: {
...DEFAULT_PREFLIGHT_CFG,
messages: {
groupChat: {
mentionPatterns: ["openclaw"],
},
},
} as import("openclaw/plugin-sdk/config-runtime").OpenClawConfig,
discordConfig: {} as DiscordConfig,
data: createGuildEvent({
channelId,
guildId,
author: message.author,
message,
}),
client,
}),
guildEntries: {
[guildId]: {
channels: {
[channelId]: {
allow: true,
requireMention: true,
users: ["user-1"],
},
},
},
},
});
expect(transcribeFirstAudioMock).not.toHaveBeenCalled();
expect(result).toBeNull();
});
it("drops guild message without mention when channel has configuredBinding and requireMention: true", async () => {
const conversationRuntime = await import("openclaw/plugin-sdk/conversation-runtime");
const channelId = "ch-binding-1";

View File

@@ -679,9 +679,22 @@ export async function preflightDiscordMessage(
shouldRequireMention: shouldRequireMentionByConfig,
bypassMentionRequirement,
});
const { hasAccessRestrictions, memberAllowed } = resolveDiscordMemberAccessState({
channelConfig,
guildInfo,
memberRoleIds,
sender,
allowNameMatching,
});
// Preflight audio transcription for mention detection in guilds.
// This allows voice notes to be checked for mentions before being dropped.
if (isGuildMessage && hasAccessRestrictions && !memberAllowed) {
logDebug(`[discord-preflight] drop: member not allowed`);
// Keep stable Discord user IDs out of routine deny-path logs.
logVerbose("Blocked discord guild sender (not in users/roles allowlist)");
return null;
}
// Only authorized guild senders should reach the expensive transcription path.
const { hasTypedText, transcript: preflightTranscript } =
await resolveDiscordPreflightAudioMentionContext({
message,
@@ -725,13 +738,6 @@ export async function preflightDiscordMessage(
surface: "discord",
});
const hasControlCommandInMessage = hasControlCommand(baseText, params.cfg);
const { hasAccessRestrictions, memberAllowed } = resolveDiscordMemberAccessState({
channelConfig,
guildInfo,
memberRoleIds,
sender,
allowNameMatching,
});
if (!isDirectMessage) {
const { ownerAllowList, ownerAllowed: ownerOk } = resolveDiscordOwnerAccess({
@@ -834,12 +840,6 @@ export async function preflightDiscordMessage(
return null;
}
if (isGuildMessage && hasAccessRestrictions && !memberAllowed) {
logDebug(`[discord-preflight] drop: member not allowed`);
logVerbose(`Blocked discord guild sender ${sender.id} (not in users/roles allowlist)`);
return null;
}
const systemLocation = resolveDiscordSystemLocation({
isDirectMessage,
isGroupDm,