diff --git a/.gitignore b/.gitignore index 4608f5e..19ad939 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,3 @@ fylgja.yml slack.yml sigma.db sigma-repo/ -.VSCodeCounter \ No newline at end of file diff --git a/src/blocks/sigma/sigma_conversion_block.js b/src/blocks/sigma/sigma_conversion_block.js index 3932a9f..8190e6f 100644 --- a/src/blocks/sigma/sigma_conversion_block.js +++ b/src/blocks/sigma/sigma_conversion_block.js @@ -40,13 +40,6 @@ 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 @@ -73,19 +66,6 @@ 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 5ac5217..89ca272 100644 --- a/src/blocks/sigma/sigma_details_block.js +++ b/src/blocks/sigma/sigma_details_block.js @@ -112,7 +112,7 @@ function getSigmaRuleDetailsBlocks(details) { 'discovery': 'TA0007', 'lateralmovement': 'TA0008', 'collection': 'TA0009', - 'commandandcontrol': 'TA0011', + 'command-and-control': 'TA0011', 'exfiltration': 'TA0010', 'impact': 'TA0040' }; @@ -193,28 +193,6 @@ 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' }); @@ -317,4 +295,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 c459375..e118c17 100644 --- a/src/blocks/sigma/sigma_search_results_block.js +++ b/src/blocks/sigma/sigma_search_results_block.js @@ -8,7 +8,6 @@ const logger = require('../../utils/logger'); const { getFileName } = require('../../utils/file_utils'); -const { getProductEmoji } = require('../../utils/os_emojis'); const FILE_NAME = getFileName(__filename); /** @@ -79,15 +78,12 @@ const getSearchResultBlocks = (keyword, results, pagination = {}) => { const ruleId = safeRule.id || 'unknown'; logger.debug(`${FILE_NAME}: Adding result #${index + 1}: ${ruleId} - ${safeRule.title || 'Untitled'}`); - // Get product emoji - const osEmoji = getProductEmoji(safeRule.logsource && safeRule.logsource.product); - - // Rule information and action button - with OS emoji before title and no ID field + // Combine rule information and action button into a single line blocks.push({ "type": "section", "text": { "type": "mrkdwn", - "text": `*${osEmoji}${safeRule.title || 'Untitled Rule'}*` + "text": `*${safeRule.title || 'Untitled Rule'}*\nID: \`${ruleId}\`` }, "accessory": { "type": "button", diff --git a/src/handlers/sigma/actions/sigma_view_actions.js b/src/handlers/sigma/actions/sigma_view_actions.js index 09d07f3..d896f31 100644 --- a/src/handlers/sigma/actions/sigma_view_actions.js +++ b/src/handlers/sigma/actions/sigma_view_actions.js @@ -6,7 +6,7 @@ const logger = require('../../../utils/logger'); const { handleError } = require('../../../utils/error_handler'); const { getSigmaRuleYaml } = require('../../../services/sigma/sigma_details_service'); -const { searchSigmaRules, searchAndConvertRules } = require('../../../services/sigma/sigma_search_service'); +const { searchSigmaRules } = require('../../../services/sigma/sigma_search_service'); const { getYamlViewBlocks } = require('../../../blocks/sigma/sigma_view_yaml_block'); const { getSearchResultBlocks } = require('../../../blocks/sigma/sigma_search_results_block'); const { processRuleDetails } = require('./sigma_action_core'); @@ -62,7 +62,7 @@ const handlePaginationAction = async (body, ack, respond) => { logger.info(`${FILE_NAME}: Processing pagination request for "${keyword}" (page ${page}, size ${pageSize})`); // Perform the search with the new pagination parameters - const searchResult = await searchAndConvertRules(keyword, page, pageSize); + const searchResult = await searchSigmaRules(keyword, page, pageSize); if (!searchResult.success) { logger.error(`${FILE_NAME}: Search failed during pagination: ${searchResult.message}`); diff --git a/src/handlers/sigma/sigma_search_handler.js b/src/handlers/sigma/sigma_search_handler.js index 2932b8d..f6d9e17 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, searchAndConvertRules } = require('../../services/sigma/sigma_search_service'); +const { searchSigmaRules, searchSigmaRulesComplex } = 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 searchAndConvertRules(keyword, page, pageSize); + const searchResult = await searchSigmaRules(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,7 +240,6 @@ 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 021611b..21031a0 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,28 +62,27 @@ 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}` }; } } @@ -103,13 +102,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 { @@ -117,7 +116,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}`); @@ -127,9 +126,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, diff --git a/src/utils/os_emojis.js b/src/utils/os_emojis.js deleted file mode 100644 index 4d135e9..0000000 --- a/src/utils/os_emojis.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * os_emojis.js - * - * Provides emoji mappings for different products/platforms in Sigma rules - */ - -/** - * Get the appropriate emoji for a product - * @param {string} product - The product/platform name - * @returns {string} - The corresponding emoji string - */ -const getProductEmoji = (product) => { - if (!product) return ''; - - const productLower = product.toLowerCase(); - - // Mapping of products to their respective emojis - const emojiMap = { - 'aws': ':cloud:', - 'azure': ':cloud:', - 'bitbucket': ':bucket:', - 'cisco': ':satellite_antenna:', - 'django': ':snake:', - 'dns': ':globe_with_meridians:', - 'fortios': ':shield:', - 'gcp': ':cloud:', - 'github': ':octocat:', - 'huawei': ':satellite_antenna:', - 'juniper': ':satellite_antenna:', - 'jvm': ':coffee:', - 'kubernetes': ':wheel_of_dharma:', - 'linux': ':penguin:', - 'm365': ':envelope:', - 'macos': ':apple:', - 'modsecurity': ':shield:', - 'nodejs': ':green_heart:', - 'okta': ':key:', - 'onelogin': ':key:', - 'opencanary': ':bird:', - 'paloalto': ':shield:', - 'python': ':snake:', - 'qualys': ':mag:', - 'rpc_firewall': ':fire_extinguisher:', - 'ruby_on_rails': ':gem:', - 'spring': ':leaves:', - 'sql': ':floppy_disk:', - 'velocity': ':zap:', - 'windows': ':window:', - 'zeek': ':eyes:' - }; - - // Check if the product is directly in our map - for (const [key, emoji] of Object.entries(emojiMap)) { - if (productLower.includes(key)) { - return emoji + ' '; - } - } - - // Default emoji for unknown products - return ':computer: '; - }; - - module.exports = { - getProductEmoji - }; \ No newline at end of file