Merge pull request 'addOSandCategoryLabels' (#4) from addOSandCategoryLabels into main
Reviewed-on: https://codeberg.org/charlottecroce/fylgja/pulls/4
This commit is contained in:
commit
27d8ca32c0
8 changed files with 144 additions and 30 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ fylgja.yml
|
|||
slack.yml
|
||||
sigma.db
|
||||
sigma-repo/
|
||||
.VSCodeCounter
|
|
@ -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: {
|
||||
|
|
|
@ -112,7 +112,7 @@ function getSigmaRuleDetailsBlocks(details) {
|
|||
'discovery': 'TA0007',
|
||||
'lateralmovement': 'TA0008',
|
||||
'collection': 'TA0009',
|
||||
'command-and-control': 'TA0011',
|
||||
'commandandcontrol': 'TA0011',
|
||||
'exfiltration': 'TA0010',
|
||||
'impact': 'TA0040'
|
||||
};
|
||||
|
@ -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' });
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
const logger = require('../../utils/logger');
|
||||
|
||||
const { getFileName } = require('../../utils/file_utils');
|
||||
const { getProductEmoji } = require('../../utils/os_emojis');
|
||||
const FILE_NAME = getFileName(__filename);
|
||||
|
||||
/**
|
||||
|
@ -78,12 +79,15 @@ 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 product emoji
|
||||
const osEmoji = getProductEmoji(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",
|
||||
|
|
|
@ -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 } = require('../../../services/sigma/sigma_search_service');
|
||||
const { searchSigmaRules, searchAndConvertRules } = 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 searchSigmaRules(keyword, page, pageSize);
|
||||
const searchResult = await searchAndConvertRules(keyword, page, pageSize);
|
||||
|
||||
if (!searchResult.success) {
|
||||
logger.error(`${FILE_NAME}: Search failed during pagination: ${searchResult.message}`);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -62,10 +62,11 @@ 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 : []
|
||||
};
|
||||
|
|
65
src/utils/os_emojis.js
Normal file
65
src/utils/os_emojis.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* 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
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue