/** * sigma_stats_block.js * * Creates Slack Block Kit blocks for displaying Sigma rule database statistics */ const logger = require('../../utils/logger'); const { getFileName } = require('../../utils/file_utils'); const FILE_NAME = getFileName(__filename); /** * Create Slack block kit blocks for statistics display * * @param {Object} stats - The statistics object with all statistical data * @returns {Array} Formatted Slack blocks ready for display */ function getStatsBlocks(stats) { logger.debug(`${FILE_NAME}: Creating statistics display blocks`); if (!stats) { logger.error(`${FILE_NAME}: Failed to create statistics blocks: No stats object provided`); return [ { type: 'section', text: { type: 'mrkdwn', text: 'Error: No statistics data provided' } } ]; } // Format the date for display const formatDate = (dateString) => { if (!dateString) return 'Unknown'; try { const date = new Date(dateString); return date.toLocaleString(); } catch (error) { return dateString; } }; // Start with header block const blocks = [ { type: 'header', text: { type: 'plain_text', text: 'Sigma Rule Database Statistics', emoji: true } }, { type: 'context', elements: [ { type: 'mrkdwn', text: `Last updated: ${formatDate(stats.lastUpdate)}` } ] } ]; // Add divider for visual separation blocks.push({ type: 'divider' }); // Overall statistics section blocks.push({ type: 'section', text: { type: 'mrkdwn', text: '*Overall Statistics*' } }); blocks.push({ type: 'section', fields: [ { type: 'mrkdwn', text: `*Total Rules:* ${stats.totalRules.toLocaleString()}` }, { type: 'mrkdwn', text: `*Database Health:* ${stats.databaseHealth.contentPercentage}% Complete` } ] }); // Add divider for visual separation blocks.push({ type: 'divider' }); // Operating system breakdown blocks.push({ type: 'section', text: { type: 'mrkdwn', text: '*Rules by Operating System*' } }); blocks.push({ type: 'section', fields: [ { type: 'mrkdwn', text: `*Windows:* ${stats.operatingSystems.windows.toLocaleString()} rules` }, { type: 'mrkdwn', text: `*Linux:* ${stats.operatingSystems.linux.toLocaleString()} rules` }, { type: 'mrkdwn', text: `*macOS:* ${stats.operatingSystems.macos.toLocaleString()} rules` }, { type: 'mrkdwn', text: `*Other/Unknown:* ${stats.operatingSystems.other.toLocaleString()} rules` } ] }); // Add divider for visual separation blocks.push({ type: 'divider' }); // Severity breakdown blocks.push({ type: 'section', text: { type: 'mrkdwn', text: '*Rules by Severity Level*' } }); // Create a colorful representation of severity levels const severityEmoji = { 'critical': '🔴', 'high': '🟠', 'medium': '🟡', 'low': '🟢', 'informational': '🔵' }; let severityFields = []; stats.severityLevels.forEach(level => { const emoji = severityEmoji[level.level?.toLowerCase()] || '⚪'; severityFields.push({ type: 'mrkdwn', text: `*${emoji} ${level.level ? (level.level.charAt(0).toUpperCase() + level.level.slice(1)) : 'Unknown'}:* ${level.count.toLocaleString()} rules` }); }); // Ensure we have an even number of fields for layout if (severityFields.length % 2 !== 0) { severityFields.push({ type: 'mrkdwn', text: ' ' // Empty space to balance fields }); } blocks.push({ type: 'section', fields: severityFields }); // Add divider for visual separation blocks.push({ type: 'divider' }); // Top MITRE ATT&CK tactics if (stats.mitreTactics && stats.mitreTactics.length > 0) { blocks.push({ type: 'section', text: { type: 'mrkdwn', text: '*Top MITRE ATT&CK Tactics*' } }); const mitreFields = stats.mitreTactics.map(tactic => { // Format tactic name for better readability const formattedTactic = tactic.tactic .replace(/-/g, ' ') .split(' ') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); return { type: 'mrkdwn', text: `*${formattedTactic}:* ${tactic.count.toLocaleString()} rules` }; }); // Split into multiple sections if needed for layout for (let i = 0; i < mitreFields.length; i += 2) { const sectionFields = mitreFields.slice(i, Math.min(i + 2, mitreFields.length)); // If we have an odd number at the end, add an empty field if (sectionFields.length === 1) { sectionFields.push({ type: 'mrkdwn', text: ' ' // Empty space to balance fields }); } blocks.push({ type: 'section', fields: sectionFields }); } blocks.push({ type: 'divider' }); } // Top authors if (stats.topAuthors && stats.topAuthors.length > 0) { blocks.push({ type: 'section', text: { type: 'mrkdwn', text: '*Top Rule Authors*' } }); const authorFields = stats.topAuthors.map(author => ({ type: 'mrkdwn', text: `*${author.name || 'Unknown'}:* ${author.count.toLocaleString()} rules` })); // Split into multiple sections if needed for layout for (let i = 0; i < authorFields.length; i += 2) { const sectionFields = authorFields.slice(i, Math.min(i + 2, authorFields.length)); // If we have an odd number at the end, add an empty field if (sectionFields.length === 1) { sectionFields.push({ type: 'mrkdwn', text: ' ' // Empty space to balance fields }); } blocks.push({ type: 'section', fields: sectionFields }); } } // Add a footer blocks.push({ type: 'divider' }); blocks.push({ type: 'context', elements: [ { type: 'mrkdwn', text: 'Use `/sigma-search [keyword]` to search for specific rules and `/sigma-details [id]` to get detailed information about a rule.' } ] }); logger.debug(`${FILE_NAME}: Created ${blocks.length} blocks for statistics display`); return blocks; } module.exports = { getStatsBlocks };