diff --git a/src/blocks/sigma/sigma_conversion_block.js b/src/blocks/sigma/sigma_conversion_block.js index 8190e6f..3932a9f 100644 --- a/src/blocks/sigma/sigma_conversion_block.js +++ b/src/blocks/sigma/sigma_conversion_block.js @@ -40,6 +40,13 @@ function getConversionResultBlocks(conversionResult) { format: 'siem_rule_ndjson' }; + // Extract logsource information or use defaults + const logsource = rule.logsource || {}; + const product = logsource.product || 'N/A'; + const category = logsource.category || 'N/A'; + + logger.debug(`${FILE_NAME}: Logsource info - Product: ${product}, Category: ${category}`); + // Truncate output if it's too long for Slack let output = conversionResult.output || ''; const maxOutputLength = 2900; // Slack has a limit of ~3000 chars in a code block @@ -66,6 +73,19 @@ function getConversionResultBlocks(conversionResult) { text: `*Rule ID:* ${rule.id}\n*Description:* ${rule.description}` } }, + { + type: 'section', + fields: [ + { + type: 'mrkdwn', + text: `*OS/Product:* ${product}` + }, + { + type: 'mrkdwn', + text: `*Category:* ${category}` + } + ] + }, { type: 'section', text: { diff --git a/src/blocks/sigma/sigma_details_block.js b/src/blocks/sigma/sigma_details_block.js index 89ca272..ccf175b 100644 --- a/src/blocks/sigma/sigma_details_block.js +++ b/src/blocks/sigma/sigma_details_block.js @@ -193,6 +193,28 @@ function getSigmaRuleDetailsBlocks(details) { } ]; + // Get logsource information from the details + const logsource = details.logsource || {}; + const product = logsource.product || 'N/A'; + const category = logsource.category || 'N/A'; + + logger.debug(`${FILE_NAME}: Logsource info - Product: ${product}, Category: ${category}`); + + // Add logsource information section after severity/author + blocks.push({ + type: 'section', + fields: [ + { + type: 'mrkdwn', + text: `*OS/Product:* ${product}` + }, + { + type: 'mrkdwn', + text: `*Category:* ${category}` + } + ] + }); + // Add divider for visual separation blocks.push({ type: 'divider' }); @@ -295,4 +317,4 @@ function getSigmaRuleDetailsBlocks(details) { module.exports = { getSigmaRuleDetailsBlocks -}; +}; \ No newline at end of file diff --git a/src/blocks/sigma/sigma_search_results_block.js b/src/blocks/sigma/sigma_search_results_block.js index e118c17..6ab7aef 100644 --- a/src/blocks/sigma/sigma_search_results_block.js +++ b/src/blocks/sigma/sigma_search_results_block.js @@ -78,12 +78,25 @@ const getSearchResultBlocks = (keyword, results, pagination = {}) => { const ruleId = safeRule.id || 'unknown'; logger.debug(`${FILE_NAME}: Adding result #${index + 1}: ${ruleId} - ${safeRule.title || 'Untitled'}`); - // Combine rule information and action button into a single line + // Get OS emoji based on product + const getOsEmoji = (product) => { + if (!product) return ''; + + const productLower = product.toLowerCase(); + if (productLower.includes('windows')) return ':window: '; + if (productLower.includes('mac') || productLower.includes('apple')) return ':apple: '; + if (productLower.includes('linux')) return ':penguin: '; + return ''; + }; + + const osEmoji = getOsEmoji(safeRule.logsource && safeRule.logsource.product); + + // Rule information and action button - with OS emoji before title and no ID field blocks.push({ "type": "section", "text": { "type": "mrkdwn", - "text": `*${safeRule.title || 'Untitled Rule'}*\nID: \`${ruleId}\`` + "text": `*${osEmoji}${safeRule.title || 'Untitled Rule'}*` }, "accessory": { "type": "button", diff --git a/src/handlers/sigma/sigma_search_handler.js b/src/handlers/sigma/sigma_search_handler.js index f6d9e17..2932b8d 100644 --- a/src/handlers/sigma/sigma_search_handler.js +++ b/src/handlers/sigma/sigma_search_handler.js @@ -4,7 +4,7 @@ * Handles Sigma rule search requests from Slack commands */ -const { searchSigmaRules, searchSigmaRulesComplex } = require('../../services/sigma/sigma_search_service'); +const { searchSigmaRules, searchSigmaRulesComplex, searchAndConvertRules } = require('../../services/sigma/sigma_search_service'); const logger = require('../../utils/logger'); const { handleError } = require('../../utils/error_handler'); const { getSearchResultBlocks } = require('../../blocks/sigma/sigma_search_results_block'); @@ -69,7 +69,7 @@ const handleCommand = async (command, respond) => { }); // Search for rules using the service function with pagination - const searchResult = await searchSigmaRules(keyword, page, pageSize); + const searchResult = await searchAndConvertRules(keyword, page, pageSize); logger.debug(`${FILE_NAME}: Search result status: ${searchResult.success}`); logger.debug(`${FILE_NAME}: Found ${searchResult.results?.length || 0} results out of ${searchResult.pagination?.totalResults || 0} total matches`); logger.debug(`${FILE_NAME}: About to generate blocks for search results`); @@ -240,6 +240,7 @@ const handleComplexSearch = async (command, respond) => { ); // Replace the header to indicate it's a complex search + // TODO: should be moved to dedicated block file if (blocks && blocks.length > 0) { blocks[0] = { type: "header", diff --git a/src/services/sigma/sigma_details_service.js b/src/services/sigma/sigma_details_service.js index 21031a0..021611b 100644 --- a/src/services/sigma/sigma_details_service.js +++ b/src/services/sigma/sigma_details_service.js @@ -25,15 +25,15 @@ async function getSigmaRuleDetails(ruleId) { message: 'Missing rule ID' }; } - + logger.info(`${FILE_NAME}: Running diagnostics for rule: ${ruleId}`); logger.info(`${FILE_NAME}: Explaining rule ${ruleId}`); - + try { // Run diagnostics on the rule content first const diagnosticResult = await debugRuleContent(ruleId); logger.debug(`${FILE_NAME}: Diagnostic result: ${JSON.stringify(diagnosticResult || {})}`); - + // Convert the rule ID to a structured object const conversionResult = await convertSigmaRule(ruleId); if (!conversionResult.success) { @@ -43,9 +43,9 @@ async function getSigmaRuleDetails(ruleId) { message: conversionResult.message || `Failed to parse rule with ID ${ruleId}` }; } - + const rule = conversionResult.rule; - + // Extra safety check if (!rule) { logger.error(`${FILE_NAME}: Converted rule is null for ID ${ruleId}`); @@ -54,7 +54,7 @@ async function getSigmaRuleDetails(ruleId) { message: `Failed to process rule with ID ${ruleId}` }; } - + // Create a simplified explanation with safe access to properties const explanation = { id: rule.id || ruleId, @@ -62,27 +62,28 @@ async function getSigmaRuleDetails(ruleId) { description: rule.description || 'No description provided', author: rule.author || 'Unknown author', severity: rule.level || 'Unknown', + logsource: rule.logsource || {}, // Add this line to include logsource info detectionExplanation: extractDetectionCondition(rule), falsePositives: Array.isArray(rule.falsepositives) ? rule.falsepositives : - typeof rule.falsepositives === 'string' ? [rule.falsepositives] : - ['None specified'], + typeof rule.falsepositives === 'string' ? [rule.falsepositives] : + ['None specified'], tags: Array.isArray(rule.tags) ? rule.tags : [], references: Array.isArray(rule.references) ? rule.references : [] }; - + logger.info(`${FILE_NAME}: Successfully explained rule ${ruleId}`); logger.debug(`${FILE_NAME}: Explanation properties: ${Object.keys(explanation).join(', ')}`); - - return { - success: true, - explanation + + return { + success: true, + explanation }; } catch (error) { logger.error(`${FILE_NAME}: Error explaining rule: ${error.message}`); logger.debug(`${FILE_NAME}: Error stack: ${error.stack}`); - return { - success: false, - message: `Error explaining rule: ${error.message}` + return { + success: false, + message: `Error explaining rule: ${error.message}` }; } } @@ -102,13 +103,13 @@ async function getSigmaRuleYaml(ruleId) { message: 'Missing rule ID' }; } - + logger.info(`${FILE_NAME}: Getting YAML content for rule: ${ruleId}`); - + try { // Get YAML content from database const yamlResult = await getRuleYamlContent(ruleId); - + if (!yamlResult.success) { logger.warn(`${FILE_NAME}: Failed to retrieve YAML for rule ${ruleId}: ${yamlResult.message}`); return { @@ -116,7 +117,7 @@ async function getSigmaRuleYaml(ruleId) { message: yamlResult.message || `Failed to retrieve YAML for rule with ID ${ruleId}` }; } - + // Add extra safety check for content if (!yamlResult.content) { logger.warn(`${FILE_NAME}: YAML content is empty for rule ${ruleId}`); @@ -126,9 +127,9 @@ async function getSigmaRuleYaml(ruleId) { warning: 'YAML content is empty for this rule' }; } - + logger.debug(`${FILE_NAME}: Successfully retrieved YAML content with length: ${yamlResult.content.length}`); - + // Return the YAML content return { success: true,