From 5f113f0a842d6e6da05e3d9065d99edb40c1fdd3 Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 11 Apr 2026 20:55:23 -0400 Subject: [PATCH] fix(discord): sanitize autocomplete choices --- .../src/command/abstract-command.listener.ts | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/discord/src/command/abstract-command.listener.ts b/packages/discord/src/command/abstract-command.listener.ts index 64a460791..7e05d5569 100644 --- a/packages/discord/src/command/abstract-command.listener.ts +++ b/packages/discord/src/command/abstract-command.listener.ts @@ -8,6 +8,7 @@ import * as Sentry from "@sentry/node"; import { + type APIApplicationCommandOptionChoice, type APIUser, ApplicationCommandOptionType, GatewayDispatchEvents, @@ -42,6 +43,10 @@ export interface ExecuteCommandOptions { message?: IMessage | Message; } +const MAX_AUTOCOMPLETE_CHOICES = 25; +const MAX_CHOICE_NAME_LENGTH = 100; +const MAX_STRING_CHOICE_VALUE_LENGTH = 100; + export abstract class AbstractCommandListener { protected hooks: Map; protected readonly logger = new Logger("CommandListener"); @@ -262,10 +267,40 @@ export abstract class AbstractCommandListener { return { type: InteractionResponseType.ApplicationCommandAutocompleteResult, - data: { choices: response }, + data: { choices: this.sanitizeAutocompleteChoices(response) }, }; } + private sanitizeAutocompleteChoices( + choices: APIApplicationCommandOptionChoice[] = [] + ): APIApplicationCommandOptionChoice[] { + return choices + .flatMap((choice) => { + const valueName = + choice.value === undefined || choice.value === null ? + "" : + String(choice.value); + const name = (choice.name || valueName).trim(); + + if (!name) return []; + + if ( + typeof choice.value === "string" && + (choice.value.trim().length === 0 || + choice.value.length > MAX_STRING_CHOICE_VALUE_LENGTH) + ) + return []; + + return [ + { + ...choice, + name: name.slice(0, MAX_CHOICE_NAME_LENGTH), + }, + ]; + }) + .slice(0, MAX_AUTOCOMPLETE_CHOICES); + } + private async onInteraction(interaction: Interaction): Promise { if (interaction.isCommandInteraction()) { this.onCommand(interaction);