diff --git a/CHANGELOG.md b/CHANGELOG.md index 478ebd9..fd1e45d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Fixed +- Restrict Claude authentication slash commands to one-to-one bot DMs instead of accepting other private-channel contexts. - Preserve embed titles, descriptions, and URLs in live MCP `read-messages` results. - Prevent `!guild` server-memory responses from triggering Discord mentions from stored memory or server names. - Verify recent, legacy, and full-text-search history reads before assembling model context; reject symbolic-link files and replaced storage directories. diff --git a/src/discord/commands/auth.ts b/src/discord/commands/auth.ts index 310ef31..e235248 100644 --- a/src/discord/commands/auth.ts +++ b/src/discord/commands/auth.ts @@ -1,6 +1,7 @@ import { ChatInputCommandInteraction, Events, + InteractionContextType, Message, SlashCommandBuilder, } from "discord.js"; @@ -22,7 +23,7 @@ export const authCommand = new SlashCommandBuilder() .setName("auth") .setDescription("Manage Claudify's Claude CLI authentication") .setDefaultMemberPermissions(null) - .setDMPermission(true) + .setContexts(InteractionContextType.BotDM) .addSubcommand((subcommand) => subcommand .setName("status") @@ -87,8 +88,11 @@ function safeErrorMessage(error: unknown): string { : "Claude authentication failed."; } -export function isPrivateAuthContext(guildId: string | null): boolean { - return guildId === null; +export function isPrivateAuthContext( + guildId: string | null, + context: InteractionContextType | null, +): boolean { + return guildId === null && context === InteractionContextType.BotDM; } export type AuthTextCommand = @@ -278,7 +282,7 @@ async function handleAuthInteraction( return; } - if (!isPrivateAuthContext(interaction.guildId)) { + if (!isPrivateAuthContext(interaction.guildId, interaction.context)) { await interaction.reply({ content: "For security, use this command in a private DM.", ephemeral: true, diff --git a/tests/authTextCommand.test.mjs b/tests/authTextCommand.test.mjs index 98ca592..5bd348b 100644 --- a/tests/authTextCommand.test.mjs +++ b/tests/authTextCommand.test.mjs @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; +import { InteractionContextType } from "discord.js"; import { authCommand, @@ -7,10 +8,20 @@ import { parseAuthTextCommand, } from "../build/discord/commands/auth.js"; -test("slash auth is available only in private contexts", () => { - assert.equal(isPrivateAuthContext(null), true); - assert.equal(isPrivateAuthContext("guild-id"), false); - assert.equal(authCommand.toJSON().dm_permission, true); +test("slash auth is available only in bot DMs", () => { + assert.equal( + isPrivateAuthContext(null, InteractionContextType.BotDM), + true, + ); + assert.equal( + isPrivateAuthContext(null, InteractionContextType.PrivateChannel), + false, + ); + assert.equal( + isPrivateAuthContext("guild-id", InteractionContextType.Guild), + false, + ); + assert.deepEqual(authCommand.toJSON().contexts, [InteractionContextType.BotDM]); }); test("parses private auth text commands", () => {