add os and category labels to details and conversion slack blocks
This commit is contained in:
parent
b329988c38
commit
ad6b108d3f
5 changed files with 84 additions and 27 deletions
|
@ -40,6 +40,13 @@ function getConversionResultBlocks(conversionResult) {
|
||||||
format: 'siem_rule_ndjson'
|
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
|
// Truncate output if it's too long for Slack
|
||||||
let output = conversionResult.output || '';
|
let output = conversionResult.output || '';
|
||||||
const maxOutputLength = 2900; // Slack has a limit of ~3000 chars in a code block
|
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}`
|
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',
|
type: 'section',
|
||||||
text: {
|
text: {
|
||||||
|
|
|
@ -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
|
// Add divider for visual separation
|
||||||
blocks.push({ type: 'divider' });
|
blocks.push({ type: 'divider' });
|
||||||
|
|
||||||
|
@ -295,4 +317,4 @@ function getSigmaRuleDetailsBlocks(details) {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getSigmaRuleDetailsBlocks
|
getSigmaRuleDetailsBlocks
|
||||||
};
|
};
|
|
@ -78,12 +78,25 @@ const getSearchResultBlocks = (keyword, results, pagination = {}) => {
|
||||||
const ruleId = safeRule.id || 'unknown';
|
const ruleId = safeRule.id || 'unknown';
|
||||||
logger.debug(`${FILE_NAME}: Adding result #${index + 1}: ${ruleId} - ${safeRule.title || 'Untitled'}`);
|
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({
|
blocks.push({
|
||||||
"type": "section",
|
"type": "section",
|
||||||
"text": {
|
"text": {
|
||||||
"type": "mrkdwn",
|
"type": "mrkdwn",
|
||||||
"text": `*${safeRule.title || 'Untitled Rule'}*\nID: \`${ruleId}\``
|
"text": `*${osEmoji}${safeRule.title || 'Untitled Rule'}*`
|
||||||
},
|
},
|
||||||
"accessory": {
|
"accessory": {
|
||||||
"type": "button",
|
"type": "button",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* Handles Sigma rule search requests from Slack commands
|
* 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 logger = require('../../utils/logger');
|
||||||
const { handleError } = require('../../utils/error_handler');
|
const { handleError } = require('../../utils/error_handler');
|
||||||
const { getSearchResultBlocks } = require('../../blocks/sigma/sigma_search_results_block');
|
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
|
// 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}: 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}: 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`);
|
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
|
// Replace the header to indicate it's a complex search
|
||||||
|
// TODO: should be moved to dedicated block file
|
||||||
if (blocks && blocks.length > 0) {
|
if (blocks && blocks.length > 0) {
|
||||||
blocks[0] = {
|
blocks[0] = {
|
||||||
type: "header",
|
type: "header",
|
||||||
|
|
|
@ -25,15 +25,15 @@ async function getSigmaRuleDetails(ruleId) {
|
||||||
message: 'Missing rule ID'
|
message: 'Missing rule ID'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`${FILE_NAME}: Running diagnostics for rule: ${ruleId}`);
|
logger.info(`${FILE_NAME}: Running diagnostics for rule: ${ruleId}`);
|
||||||
logger.info(`${FILE_NAME}: Explaining rule ${ruleId}`);
|
logger.info(`${FILE_NAME}: Explaining rule ${ruleId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Run diagnostics on the rule content first
|
// Run diagnostics on the rule content first
|
||||||
const diagnosticResult = await debugRuleContent(ruleId);
|
const diagnosticResult = await debugRuleContent(ruleId);
|
||||||
logger.debug(`${FILE_NAME}: Diagnostic result: ${JSON.stringify(diagnosticResult || {})}`);
|
logger.debug(`${FILE_NAME}: Diagnostic result: ${JSON.stringify(diagnosticResult || {})}`);
|
||||||
|
|
||||||
// Convert the rule ID to a structured object
|
// Convert the rule ID to a structured object
|
||||||
const conversionResult = await convertSigmaRule(ruleId);
|
const conversionResult = await convertSigmaRule(ruleId);
|
||||||
if (!conversionResult.success) {
|
if (!conversionResult.success) {
|
||||||
|
@ -43,9 +43,9 @@ async function getSigmaRuleDetails(ruleId) {
|
||||||
message: conversionResult.message || `Failed to parse rule with ID ${ruleId}`
|
message: conversionResult.message || `Failed to parse rule with ID ${ruleId}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const rule = conversionResult.rule;
|
const rule = conversionResult.rule;
|
||||||
|
|
||||||
// Extra safety check
|
// Extra safety check
|
||||||
if (!rule) {
|
if (!rule) {
|
||||||
logger.error(`${FILE_NAME}: Converted rule is null for ID ${ruleId}`);
|
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}`
|
message: `Failed to process rule with ID ${ruleId}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a simplified explanation with safe access to properties
|
// Create a simplified explanation with safe access to properties
|
||||||
const explanation = {
|
const explanation = {
|
||||||
id: rule.id || ruleId,
|
id: rule.id || ruleId,
|
||||||
|
@ -62,27 +62,28 @@ async function getSigmaRuleDetails(ruleId) {
|
||||||
description: rule.description || 'No description provided',
|
description: rule.description || 'No description provided',
|
||||||
author: rule.author || 'Unknown author',
|
author: rule.author || 'Unknown author',
|
||||||
severity: rule.level || 'Unknown',
|
severity: rule.level || 'Unknown',
|
||||||
|
logsource: rule.logsource || {}, // Add this line to include logsource info
|
||||||
detectionExplanation: extractDetectionCondition(rule),
|
detectionExplanation: extractDetectionCondition(rule),
|
||||||
falsePositives: Array.isArray(rule.falsepositives) ? rule.falsepositives :
|
falsePositives: Array.isArray(rule.falsepositives) ? rule.falsepositives :
|
||||||
typeof rule.falsepositives === 'string' ? [rule.falsepositives] :
|
typeof rule.falsepositives === 'string' ? [rule.falsepositives] :
|
||||||
['None specified'],
|
['None specified'],
|
||||||
tags: Array.isArray(rule.tags) ? rule.tags : [],
|
tags: Array.isArray(rule.tags) ? rule.tags : [],
|
||||||
references: Array.isArray(rule.references) ? rule.references : []
|
references: Array.isArray(rule.references) ? rule.references : []
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.info(`${FILE_NAME}: Successfully explained rule ${ruleId}`);
|
logger.info(`${FILE_NAME}: Successfully explained rule ${ruleId}`);
|
||||||
logger.debug(`${FILE_NAME}: Explanation properties: ${Object.keys(explanation).join(', ')}`);
|
logger.debug(`${FILE_NAME}: Explanation properties: ${Object.keys(explanation).join(', ')}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
explanation
|
explanation
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`${FILE_NAME}: Error explaining rule: ${error.message}`);
|
logger.error(`${FILE_NAME}: Error explaining rule: ${error.message}`);
|
||||||
logger.debug(`${FILE_NAME}: Error stack: ${error.stack}`);
|
logger.debug(`${FILE_NAME}: Error stack: ${error.stack}`);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: `Error explaining rule: ${error.message}`
|
message: `Error explaining rule: ${error.message}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,13 +103,13 @@ async function getSigmaRuleYaml(ruleId) {
|
||||||
message: 'Missing rule ID'
|
message: 'Missing rule ID'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`${FILE_NAME}: Getting YAML content for rule: ${ruleId}`);
|
logger.info(`${FILE_NAME}: Getting YAML content for rule: ${ruleId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get YAML content from database
|
// Get YAML content from database
|
||||||
const yamlResult = await getRuleYamlContent(ruleId);
|
const yamlResult = await getRuleYamlContent(ruleId);
|
||||||
|
|
||||||
if (!yamlResult.success) {
|
if (!yamlResult.success) {
|
||||||
logger.warn(`${FILE_NAME}: Failed to retrieve YAML for rule ${ruleId}: ${yamlResult.message}`);
|
logger.warn(`${FILE_NAME}: Failed to retrieve YAML for rule ${ruleId}: ${yamlResult.message}`);
|
||||||
return {
|
return {
|
||||||
|
@ -116,7 +117,7 @@ async function getSigmaRuleYaml(ruleId) {
|
||||||
message: yamlResult.message || `Failed to retrieve YAML for rule with ID ${ruleId}`
|
message: yamlResult.message || `Failed to retrieve YAML for rule with ID ${ruleId}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add extra safety check for content
|
// Add extra safety check for content
|
||||||
if (!yamlResult.content) {
|
if (!yamlResult.content) {
|
||||||
logger.warn(`${FILE_NAME}: YAML content is empty for rule ${ruleId}`);
|
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'
|
warning: 'YAML content is empty for this rule'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`${FILE_NAME}: Successfully retrieved YAML content with length: ${yamlResult.content.length}`);
|
logger.debug(`${FILE_NAME}: Successfully retrieved YAML content with length: ${yamlResult.content.length}`);
|
||||||
|
|
||||||
// Return the YAML content
|
// Return the YAML content
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue