diff --git a/src/controllers/chatbotController.js b/src/controllers/chatbotController.js index 2fef9ea..2be5fb5 100644 --- a/src/controllers/chatbotController.js +++ b/src/controllers/chatbotController.js @@ -166,7 +166,7 @@ async function handleChatbotWebhook(req, res, providerName) { try { const errorMessage = sanitizeBotMentions( - `❌ Sorry, only authorized users can trigger Claude commands.` + '❌ Sorry, only authorized users can trigger Claude commands.' ); await provider.sendResponse(messageContext, errorMessage); } catch (responseError) { @@ -206,8 +206,8 @@ async function handleChatbotWebhook(req, res, providerName) { // Validate required repository parameter if (!repoFullName) { const errorMessage = sanitizeBotMentions( - `❌ **Repository Required**: Please specify a repository using the \`repo\` parameter.\n\n` + - `**Example:** \`/claude repo:owner/repository command:fix this issue\`` + '❌ **Repository Required**: Please specify a repository using the `repo` parameter.\n\n' + + '**Example:** `/claude repo:owner/repository command:fix this issue`' ); await provider.sendResponse(messageContext, errorMessage); diff --git a/src/providers/ChatbotProvider.js b/src/providers/ChatbotProvider.js index 2553b46..baf63f8 100644 --- a/src/providers/ChatbotProvider.js +++ b/src/providers/ChatbotProvider.js @@ -21,7 +21,7 @@ class ChatbotProvider { * @param {Object} req - Express request object * @returns {boolean} - True if signature is valid */ - verifyWebhookSignature(req) { + verifyWebhookSignature(_req) { throw new Error('verifyWebhookSignature() must be implemented by subclass'); } @@ -30,7 +30,7 @@ class ChatbotProvider { * @param {Object} payload - Raw webhook payload * @returns {Object} - Standardized message object */ - parseWebhookPayload(payload) { + parseWebhookPayload(_payload) { throw new Error('parseWebhookPayload() must be implemented by subclass'); } @@ -39,7 +39,7 @@ class ChatbotProvider { * @param {string} message - Message content * @returns {Object|null} - Command object or null if no mention */ - extractBotCommand(message) { + extractBotCommand(_message) { throw new Error('extractBotCommand() must be implemented by subclass'); } @@ -49,7 +49,7 @@ class ChatbotProvider { * @param {string} response - Response text * @returns {Promise} */ - async sendResponse(context, response) { + async sendResponse(_context, _response) { throw new Error('sendResponse() must be implemented by subclass'); } @@ -58,7 +58,7 @@ class ChatbotProvider { * @param {Object} context - Message context * @returns {string} - User identifier */ - getUserId(context) { + getUserId(_context) { throw new Error('getUserId() must be implemented by subclass'); } diff --git a/src/providers/DiscordProvider.js b/src/providers/DiscordProvider.js index 4e5b86d..4ac6055 100644 --- a/src/providers/DiscordProvider.js +++ b/src/providers/DiscordProvider.js @@ -90,48 +90,49 @@ class DiscordProvider extends ChatbotProvider { try { // Handle Discord interaction types switch (payload.type) { - case 1: // PING - return { - type: 'ping', - shouldRespond: true, - responseData: { type: 1 } // PONG - }; + case 1: // PING + return { + type: 'ping', + shouldRespond: true, + responseData: { type: 1 } // PONG + }; - case 2: // APPLICATION_COMMAND - const repoInfo = this.extractRepoAndBranch(payload.data); - return { - type: 'command', - command: payload.data?.name, - options: payload.data?.options || [], - channelId: payload.channel_id, - guildId: payload.guild_id, - userId: payload.member?.user?.id || payload.user?.id, - username: payload.member?.user?.username || payload.user?.username, - content: this.buildCommandContent(payload.data), - interactionToken: payload.token, - interactionId: payload.id, - repo: repoInfo.repo, - branch: repoInfo.branch - }; + case 2: { // APPLICATION_COMMAND + const repoInfo = this.extractRepoAndBranch(payload.data); + return { + type: 'command', + command: payload.data?.name, + options: payload.data?.options || [], + channelId: payload.channel_id, + guildId: payload.guild_id, + userId: payload.member?.user?.id || payload.user?.id, + username: payload.member?.user?.username || payload.user?.username, + content: this.buildCommandContent(payload.data), + interactionToken: payload.token, + interactionId: payload.id, + repo: repoInfo.repo, + branch: repoInfo.branch + }; + } - case 3: // MESSAGE_COMPONENT - return { - type: 'component', - customId: payload.data?.custom_id, - channelId: payload.channel_id, - guildId: payload.guild_id, - userId: payload.member?.user?.id || payload.user?.id, - username: payload.member?.user?.username || payload.user?.username, - interactionToken: payload.token, - interactionId: payload.id - }; + case 3: // MESSAGE_COMPONENT + return { + type: 'component', + customId: payload.data?.custom_id, + channelId: payload.channel_id, + guildId: payload.guild_id, + userId: payload.member?.user?.id || payload.user?.id, + username: payload.member?.user?.username || payload.user?.username, + interactionToken: payload.token, + interactionId: payload.id + }; - default: - logger.warn({ type: payload.type }, 'Unknown Discord interaction type'); - return { - type: 'unknown', - shouldRespond: false - }; + default: + logger.warn({ type: payload.type }, 'Unknown Discord interaction type'); + return { + type: 'unknown', + shouldRespond: false + }; } } catch (error) { logger.error({ err: error }, 'Error parsing Discord webhook payload'); @@ -327,10 +328,10 @@ class DiscordProvider extends ChatbotProvider { */ formatErrorMessage(error, errorId) { const timestamp = new Date().toISOString(); - return `🚫 **Error Processing Command**\n\n` + + return '🚫 **Error Processing Command**\n\n' + `**Reference ID:** \`${errorId}\`\n` + `**Time:** ${timestamp}\n\n` + - `Please contact an administrator with the reference ID above.`; + 'Please contact an administrator with the reference ID above.'; } /** diff --git a/src/providers/ProviderFactory.js b/src/providers/ProviderFactory.js index 6264764..2c7d234 100644 --- a/src/providers/ProviderFactory.js +++ b/src/providers/ProviderFactory.js @@ -160,15 +160,15 @@ class ProviderFactory { // Provider-specific environment variables switch (providerName) { - case 'discord': - config.botToken = process.env.DISCORD_BOT_TOKEN; - config.publicKey = process.env.DISCORD_PUBLIC_KEY; - config.applicationId = process.env.DISCORD_APPLICATION_ID; - config.authorizedUsers = process.env.DISCORD_AUTHORIZED_USERS?.split(',').map(u => u.trim()); - config.botMention = process.env.DISCORD_BOT_MENTION; - break; - default: - throw new Error(`Unsupported provider: ${providerName}. Only 'discord' is currently supported.`); + case 'discord': + config.botToken = process.env.DISCORD_BOT_TOKEN; + config.publicKey = process.env.DISCORD_PUBLIC_KEY; + config.applicationId = process.env.DISCORD_APPLICATION_ID; + config.authorizedUsers = process.env.DISCORD_AUTHORIZED_USERS?.split(',').map(u => u.trim()); + config.botMention = process.env.DISCORD_BOT_MENTION; + break; + default: + throw new Error(`Unsupported provider: ${providerName}. Only 'discord' is currently supported.`); } // Remove undefined values diff --git a/src/routes/chatbot.js b/src/routes/chatbot.js index 1a4f742..9f70874 100644 --- a/src/routes/chatbot.js +++ b/src/routes/chatbot.js @@ -15,7 +15,7 @@ const chatbotLimiter = rateLimit({ }, standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers - skip: (req) => { + skip: (_req) => { // Skip rate limiting in test environment return process.env.NODE_ENV === 'test'; } diff --git a/test/unit/providers/ProviderFactory.test.js b/test/unit/providers/ProviderFactory.test.js index 3bf30ed..92cb198 100644 --- a/test/unit/providers/ProviderFactory.test.js +++ b/test/unit/providers/ProviderFactory.test.js @@ -13,7 +13,7 @@ jest.mock('../../../src/utils/secureCredentials', () => ({ loadCredentials: jest.fn() })); -const ProviderFactory = require('../../../src/providers/ProviderFactory'); +const _ProviderFactory = require('../../../src/providers/ProviderFactory'); const DiscordProvider = require('../../../src/providers/DiscordProvider'); const ChatbotProvider = require('../../../src/providers/ChatbotProvider'); @@ -202,7 +202,7 @@ describe('ProviderFactory', () => { expect(config).toEqual({ botToken: 'test_token' }); - expect(config.hasOwnProperty('publicKey')).toBe(false); + expect(Object.prototype.hasOwnProperty.call(config, 'publicKey')).toBe(false); }); }); diff --git a/test/unit/security/signature-verification.test.js b/test/unit/security/signature-verification.test.js index 8bfa069..7c4aa22 100644 --- a/test/unit/security/signature-verification.test.js +++ b/test/unit/security/signature-verification.test.js @@ -20,7 +20,7 @@ const mockSecureCredentials = require('../../../src/utils/secureCredentials'); describe.skip('Signature Verification Security Tests', () => { let provider; const validPublicKey = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; - const validPrivateKey = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789'; + const _validPrivateKey = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789'; // Helper function to run test with production NODE_ENV const withProductionEnv = (testFn) => {