fix: keep Discord DM wildcard out of owner checks

This commit is contained in:
Peter Steinberger
2026-04-29 15:44:13 +01:00
parent 04f651b783
commit 7acb78852f
4 changed files with 56 additions and 1 deletions

View File

@@ -10,6 +10,7 @@ import {
resolveDiscordChannelConfig,
resolveDiscordChannelConfigWithFallback,
resolveDiscordGuildEntry,
resolveDiscordOwnerAccess,
resolveDiscordShouldRequireMention,
resolveGroupDmAllow,
shouldEmitDiscordReactionNotification,
@@ -252,6 +253,22 @@ describe("discord allowlist helpers", () => {
expect(allowListMatches(allow, { id: "member-123" })).toBe(true);
expect(allowListMatches(allow, { id: "member-999" })).toBe(false);
});
it("does not treat DM wildcard access as owner access", () => {
const wildcardOnly = resolveDiscordOwnerAccess({
allowFrom: ["*"],
sender: { id: "123" },
});
expect(wildcardOnly.ownerAllowList).toBeNull();
expect(wildcardOnly.ownerAllowed).toBe(false);
const explicitOwner = resolveDiscordOwnerAccess({
allowFrom: ["*", "user:123"],
sender: { id: "123" },
});
expect(explicitOwner.ownerAllowList).not.toBeNull();
expect(explicitOwner.ownerAllowed).toBe(true);
});
});
describe("discord guild/channel resolution", () => {

View File

@@ -279,8 +279,11 @@ export function resolveDiscordOwnerAccess(params: {
ownerAllowList: DiscordAllowList | null;
ownerAllowed: boolean;
} {
const ownerAllowFrom = params.allowFrom?.filter(
(entry) => (normalizeOptionalString(entry) ?? "") !== "*",
);
const ownerAllowList = normalizeDiscordAllowList(
params.allowFrom,
ownerAllowFrom && ownerAllowFrom.length > 0 ? ownerAllowFrom : undefined,
DISCORD_OWNER_ALLOWLIST_PREFIXES,
);
const ownerAllowed = ownerAllowList

View File

@@ -342,6 +342,39 @@ describe("Discord native slash commands with commands.allowFrom", () => {
expectUnauthorizedReply(interaction);
});
it("does not treat open-DM wildcard access as guild command owner authorization", async () => {
const { dispatchSpy, interaction } = await runGuildSlashCommand({
userId: "999999999999999999",
mutateConfig: (cfg) => {
cfg.commands = {
...cfg.commands,
useAccessGroups: false,
allowFrom: undefined,
};
cfg.channels = {
...cfg.channels,
discord: {
...cfg.channels?.discord,
dmPolicy: "open",
allowFrom: ["*"],
guilds: {
"000000000000000000": {
channels: {
"111111111111111111": {
enabled: true,
requireMention: false,
},
},
},
},
},
};
},
});
expect(dispatchSpy).not.toHaveBeenCalled();
expectUnauthorizedReply(interaction);
});
it("rejects guild slash commands when commands.allowFrom.discord does not match the sender", async () => {
const { dispatchSpy, interaction } = await runGuildSlashCommand({
userId: "999999999999999999",

View File

@@ -174,6 +174,8 @@ describe("monitorDiscordProvider", () => {
vi.doMock("../accounts.js", () => ({
resolveDiscordAccount: (...args: Parameters<typeof resolveDiscordAccountMock>) =>
resolveDiscordAccountMock(...args),
resolveDiscordAccountAllowFrom: () => undefined,
resolveDiscordAccountDmPolicy: () => undefined,
}));
vi.doMock("../probe.js", () => ({
fetchDiscordApplicationId: async () => "app-1",