fix(discord): fail-fast when bot identity fetch fails
When fetchUser('@me') fails during Discord provider startup (e.g. due to
transient network issues), the bot previously continued running with
botUserId = undefined. This caused three security issues:
1. Mention gating bypassed: the guard 'if (botId && mentionGate.shouldSkip)'
short-circuited when botId was undefined, letting all guild messages
through even with requireMention: true.
2. Self-message filtering disabled: 'author.id === botUserId' never matched,
risking self-reply loops.
3. Reply detection broken: replies to bot messages weren't recognized as
implicit mentions.
Fix:
- Throw on fetchUser failure so auto-restart retries (matching the existing
pattern for fetchDiscordApplicationId).
- Also throw when fetchUser returns no id.
- Defense-in-depth: remove the redundant 'botId &&' guard in the mention
gate check so mentionGate.shouldSkip is evaluated regardless.
Fixes #42219
This commit is contained in:
parent
4c60956d8e
commit
cd0d45b184
@ -788,7 +788,7 @@ export async function preflightDiscordMessage(
|
||||
`[discord-preflight] shouldRequireMention=${shouldRequireMention} baseRequireMention=${shouldRequireMentionByConfig} boundThreadSession=${isBoundThreadSession} mentionGate.shouldSkip=${mentionGate.shouldSkip} wasMentioned=${wasMentioned}`,
|
||||
);
|
||||
if (isGuildMessage && shouldRequireMention) {
|
||||
if (botId && mentionGate.shouldSkip) {
|
||||
if (mentionGate.shouldSkip) {
|
||||
logDebug(`[discord-preflight] drop: no-mention`);
|
||||
logVerbose(`discord: drop guild message (mention required, botId=${botId})`);
|
||||
logger.info(
|
||||
|
||||
@ -627,4 +627,32 @@ describe("monitorDiscordProvider", () => {
|
||||
const messages = vi.mocked(runtime.log).mock.calls.map((call) => String(call[0]));
|
||||
expect(messages.some((msg) => msg.includes("discord startup ["))).toBe(false);
|
||||
});
|
||||
|
||||
it("throws when fetchUser('@me') fails to prevent running without bot identity (#42219)", async () => {
|
||||
const { monitorDiscordProvider } = await import("./provider.js");
|
||||
const runtime = baseRuntime();
|
||||
|
||||
clientFetchUserMock.mockRejectedValueOnce(new Error("network timeout"));
|
||||
|
||||
await expect(
|
||||
monitorDiscordProvider({
|
||||
config: baseConfig(),
|
||||
runtime,
|
||||
}),
|
||||
).rejects.toThrow("discord: cannot start without bot identity");
|
||||
});
|
||||
|
||||
it("throws when fetchUser('@me') returns no user id", async () => {
|
||||
const { monitorDiscordProvider } = await import("./provider.js");
|
||||
const runtime = baseRuntime();
|
||||
|
||||
clientFetchUserMock.mockResolvedValueOnce({ id: undefined, username: "NoId" });
|
||||
|
||||
await expect(
|
||||
monitorDiscordProvider({
|
||||
config: baseConfig(),
|
||||
runtime,
|
||||
}),
|
||||
).rejects.toThrow("discord: fetchUser('@me') returned no user id");
|
||||
});
|
||||
});
|
||||
|
||||
@ -880,6 +880,15 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
|
||||
gateway: lifecycleGateway,
|
||||
details: String(err),
|
||||
});
|
||||
// Fail-fast: without botUserId, mention gating is bypassed (all guild
|
||||
// messages pass through), self-message filtering is disabled (risk of
|
||||
// self-reply loops), and reply detection is broken. Let auto-restart
|
||||
// retry instead of running in a degraded state. See #42219.
|
||||
throw new Error(`discord: cannot start without bot identity: ${String(err)}`);
|
||||
}
|
||||
if (!botUserId) {
|
||||
// fetchUser succeeded but returned no id — equally unsafe to continue.
|
||||
throw new Error("discord: fetchUser('@me') returned no user id");
|
||||
}
|
||||
|
||||
if (voiceEnabled) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user