fix(discord): canonicalize resolved allowlists to ids

This commit is contained in:
Peter Steinberger
2026-02-21 19:53:00 +01:00
parent 3ed71d6f76
commit 747bb581b3
5 changed files with 168 additions and 30 deletions

View File

@@ -0,0 +1,57 @@
import { describe, expect, it, vi } from "vitest";
import type { RuntimeEnv } from "../../runtime.js";
const { resolveDiscordChannelAllowlistMock, resolveDiscordUserAllowlistMock } = vi.hoisted(() => ({
resolveDiscordChannelAllowlistMock: vi.fn(async () => []),
resolveDiscordUserAllowlistMock: vi.fn(async (params: { entries: string[] }) =>
params.entries.map((entry) => {
switch (entry) {
case "Alice":
return { input: entry, resolved: true, id: "111" };
case "Bob":
return { input: entry, resolved: true, id: "222" };
case "Carol":
return { input: entry, resolved: false };
default:
return { input: entry, resolved: true, id: entry };
}
}),
),
}));
vi.mock("../resolve-channels.js", () => ({
resolveDiscordChannelAllowlist: resolveDiscordChannelAllowlistMock,
}));
vi.mock("../resolve-users.js", () => ({
resolveDiscordUserAllowlist: resolveDiscordUserAllowlistMock,
}));
import { resolveDiscordAllowlistConfig } from "./provider.allowlist.js";
describe("resolveDiscordAllowlistConfig", () => {
it("canonicalizes resolved user names to ids in runtime config", async () => {
const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() } as unknown as RuntimeEnv;
const result = await resolveDiscordAllowlistConfig({
token: "token",
allowFrom: ["Alice", "111", "*"],
guildEntries: {
"*": {
users: ["Bob", "999"],
channels: {
"*": {
users: ["Carol", "888"],
},
},
},
},
fetcher: vi.fn() as unknown as typeof fetch,
runtime,
});
expect(result.allowFrom).toEqual(["111", "*"]);
expect(result.guildEntries?.["*"]?.users).toEqual(["222", "999"]);
expect(result.guildEntries?.["*"]?.channels?.["*"]?.users).toEqual(["Carol", "888"]);
expect(resolveDiscordUserAllowlistMock).toHaveBeenCalledTimes(2);
});
});

View File

@@ -1,9 +1,8 @@
import {
addAllowlistUserEntriesFromConfigEntry,
buildAllowlistResolutionSummary,
mergeAllowlist,
canonicalizeAllowlistWithResolvedIds,
patchAllowlistUsersInConfigEntries,
resolveAllowlistIdAdditions,
summarizeMapping,
} from "../../channels/allowlists/resolve-utils.js";
import type { DiscordGuildEntry } from "../../config/types.discord.js";
@@ -138,8 +137,11 @@ export async function resolveDiscordAllowlistConfig(params: {
entries: allowEntries.map((entry) => String(entry)),
fetcher: params.fetcher,
});
const { mapping, unresolved, additions } = buildAllowlistResolutionSummary(resolvedUsers);
allowFrom = mergeAllowlist({ existing: allowFrom, additions });
const { resolvedMap, mapping, unresolved } = buildAllowlistResolutionSummary(resolvedUsers);
allowFrom = canonicalizeAllowlistWithResolvedIds({
existing: allowFrom,
resolvedMap,
});
summarizeMapping("discord users", mapping, unresolved, params.runtime);
} catch (err) {
params.runtime.log?.(
@@ -178,14 +180,17 @@ export async function resolveDiscordAllowlistConfig(params: {
const nextGuild = { ...guildConfig } as Record<string, unknown>;
const users = (guildConfig as { users?: string[] }).users;
if (Array.isArray(users) && users.length > 0) {
const additions = resolveAllowlistIdAdditions({ existing: users, resolvedMap });
nextGuild.users = mergeAllowlist({ existing: users, additions });
nextGuild.users = canonicalizeAllowlistWithResolvedIds({
existing: users,
resolvedMap,
});
}
const channels = (guildConfig as { channels?: Record<string, unknown> }).channels ?? {};
if (channels && typeof channels === "object") {
nextGuild.channels = patchAllowlistUsersInConfigEntries({
entries: channels,
resolvedMap,
strategy: "canonicalize",
});
}
nextGuilds[guildKey] = nextGuild as DiscordGuildEntry;