209 lines
No EOL
6.6 KiB
JavaScript
209 lines
No EOL
6.6 KiB
JavaScript
/**
|
|
* sigma_search_results_block.js
|
|
*
|
|
* Generates Slack Block Kit blocks for displaying Sigma rule search results
|
|
* Includes pagination controls for navigating large result sets
|
|
*
|
|
*/
|
|
const logger = require('../../utils/logger');
|
|
|
|
const { getFileName } = require('../../utils/file_utils');
|
|
const { getProductEmoji } = require('../../utils/os_emojis');
|
|
const FILE_NAME = getFileName(__filename);
|
|
|
|
/**
|
|
* Generate blocks for Slack UI to display search results with pagination
|
|
*
|
|
* @param {string} keyword - The search keyword used for the query
|
|
* @param {Array} results - Array of rule results from the search
|
|
* @param {Object} pagination - Pagination information object
|
|
* @returns {Array} - Slack blocks for displaying results
|
|
*/
|
|
const getSearchResultBlocks = (keyword, results, pagination = {}) => {
|
|
|
|
logger.debug(`${FILE_NAME}: Creating search result blocks for keyword: "${keyword}"`);
|
|
|
|
// Add debug for input validation
|
|
logger.debug(`${FILE_NAME}: Results type: ${typeof results}, isArray: ${Array.isArray(results)}, length: ${Array.isArray(results) ? results.length : 'N/A'}`);
|
|
logger.debug(`${FILE_NAME}: Pagination: ${JSON.stringify(pagination)}`);
|
|
|
|
// Ensure results is always an array
|
|
const safeResults = Array.isArray(results) ? results : [];
|
|
|
|
// Default pagination values if not provided
|
|
const pagingInfo = {
|
|
currentPage: pagination.currentPage || 1,
|
|
pageSize: pagination.pageSize || 10,
|
|
totalPages: pagination.totalPages || 0,
|
|
totalResults: pagination.totalResults || 0,
|
|
hasMore: pagination.hasMore || false
|
|
};
|
|
|
|
logger.debug(`${FILE_NAME}: Processing ${safeResults.length} search results (page ${pagingInfo.currentPage} of ${pagingInfo.totalPages}, total: ${pagingInfo.totalResults})`);
|
|
|
|
// Initialize with header block that includes pagination info
|
|
const blocks = [
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `*Search Results for "${keyword}"*\n${
|
|
pagingInfo.totalResults > 0
|
|
? `Showing ${safeResults.length} of ${pagingInfo.totalResults} matching rules (page ${pagingInfo.currentPage} of ${pagingInfo.totalPages})`
|
|
: `Found ${safeResults.length} matching rules:`
|
|
}`
|
|
}
|
|
}
|
|
];
|
|
|
|
// Debug log as we build blocks
|
|
logger.debug(`${FILE_NAME}: Added header block`);
|
|
|
|
// Add blocks for each result if we have any
|
|
if (safeResults.length === 0) {
|
|
logger.debug(`${FILE_NAME}: No search results to display`);
|
|
blocks.push({
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": pagingInfo.totalResults > 0
|
|
? "No rules on this page. Try a different page."
|
|
: "No matching rules found."
|
|
}
|
|
});
|
|
} else {
|
|
logger.debug(`${FILE_NAME}: Creating blocks for ${safeResults.length} search results`);
|
|
safeResults.forEach((rule, index) => {
|
|
// Ensure rule is an object with expected properties
|
|
const safeRule = rule || {};
|
|
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
|
|
blocks.push({
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `*${osEmoji}${safeRule.title || 'Untitled Rule'}*`
|
|
},
|
|
"accessory": {
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Details",
|
|
"emoji": true
|
|
},
|
|
"value": ruleId,
|
|
"action_id": "view_rule_details"
|
|
}
|
|
});
|
|
|
|
// Add a divider between results (except after the last one)
|
|
if (index < safeResults.length - 1) {
|
|
blocks.push({
|
|
"type": "divider"
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// Debug log for pagination controls
|
|
logger.debug(`${FILE_NAME}: Checking if pagination controls needed (totalPages: ${pagingInfo.totalPages})`);
|
|
|
|
// Add pagination navigation if there are multiple pages
|
|
if (pagingInfo.totalPages > 1) {
|
|
// Add a divider before pagination controls
|
|
blocks.push({
|
|
"type": "divider"
|
|
});
|
|
|
|
// Create pagination navigation buttons
|
|
const paginationButtons = [];
|
|
|
|
// Previous page button (if not on first page)
|
|
if (pagingInfo.currentPage > 1) {
|
|
paginationButtons.push({
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Previous",
|
|
"emoji": true
|
|
},
|
|
"value": JSON.stringify({
|
|
keyword,
|
|
page: pagingInfo.currentPage - 1,
|
|
pageSize: pagingInfo.pageSize
|
|
}),
|
|
"action_id": "search_prev_page"
|
|
});
|
|
logger.debug(`${FILE_NAME}: Added Previous page button for page ${pagingInfo.currentPage - 1}`);
|
|
}
|
|
|
|
// Next page button (if there are more pages)
|
|
if (pagingInfo.hasMore) {
|
|
paginationButtons.push({
|
|
"type": "button",
|
|
"text": {
|
|
"type": "plain_text",
|
|
"text": "Next",
|
|
"emoji": true
|
|
},
|
|
"value": JSON.stringify({
|
|
keyword,
|
|
page: pagingInfo.currentPage + 1,
|
|
pageSize: pagingInfo.pageSize
|
|
}),
|
|
"action_id": "search_next_page"
|
|
});
|
|
logger.debug(`${FILE_NAME}: Added Next page button for page ${pagingInfo.currentPage + 1}`);
|
|
}
|
|
|
|
// Add the pagination buttons block if we have buttons to show
|
|
if (paginationButtons.length > 0) {
|
|
blocks.push({
|
|
"type": "actions",
|
|
"elements": paginationButtons
|
|
});
|
|
logger.debug(`${FILE_NAME}: Added ${paginationButtons.length} pagination buttons`);
|
|
}
|
|
|
|
// Add page indicator text
|
|
blocks.push({
|
|
"type": "context",
|
|
"elements": [
|
|
{
|
|
"type": "plain_text",
|
|
"text": `Page ${pagingInfo.currentPage} of ${pagingInfo.totalPages}`,
|
|
"emoji": true
|
|
}
|
|
]
|
|
});
|
|
logger.debug(`${FILE_NAME}: Added page indicator text`);
|
|
}
|
|
|
|
logger.debug(`${FILE_NAME}: Created ${blocks.length} blocks for search results`);
|
|
|
|
// Final validation of blocks array
|
|
if (!Array.isArray(blocks) || blocks.length === 0) {
|
|
logger.error(`${FILE_NAME}: Generated blocks is not a valid array or is empty`);
|
|
// Return a minimal valid blocks array
|
|
return [
|
|
{
|
|
"type": "section",
|
|
"text": {
|
|
"type": "mrkdwn",
|
|
"text": `Search Results for "${keyword}": Unable to generate proper blocks. Please try again.`
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
return blocks;
|
|
};
|
|
|
|
module.exports = {
|
|
getSearchResultBlocks
|
|
}; |