/** * rule-retrieval.js * Functions for retrieving Sigma rules and rule IDs */ const { getDbConnection } = require('../sigma_db_connection'); const logger = require('../../utils/logger'); const { getFileName } = require('../../utils/file_utils'); const FILE_NAME = getFileName(__filename); /** * Get a list of all rule IDs in the database * Useful for bulk operations and database integrity checks * * @returns {Promise} Array of rule IDs or empty array on error */ async function getAllRuleIds() { let db; try { logger.info(`${FILE_NAME}: Retrieving all rule IDs from database`); db = await getDbConnection(); logger.debug(`${FILE_NAME}: Connected to database for retrieving all rule IDs`); const result = await new Promise((resolve, reject) => { db.all('SELECT id FROM sigma_rules ORDER BY id', [], (err, rows) => { if (err) { logger.error(`${FILE_NAME}: Error fetching all rule IDs: ${err.message}`); reject(err); } else { resolve(rows || []); } }); }); logger.debug(`${FILE_NAME}: Retrieved ${result.length} rule IDs from database`); return result.map(row => row.id); } catch (error) { logger.error(`${FILE_NAME}: Error retrieving all rule IDs: ${error.message}`); logger.debug(`${FILE_NAME}: Error stack: ${error.stack}`); return []; } finally { if (db) { try { await db.close(); logger.debug(`${FILE_NAME}: Database connection closed after retrieving all rule IDs`); } catch (closeError) { logger.warn(`${FILE_NAME}: Error closing database: ${closeError.message}`); } } } } /** * Find a Sigma rule by its ID * Retrieves rule data and associated parameters from the database * * @param {string} ruleId - The ID of the rule to find * @returns {Promise} The rule object or null if not found */ async function findRuleById(ruleId) { if (!ruleId) { logger.warn(`${FILE_NAME}: Cannot find rule: Missing rule ID`); return null; } let db; try { db = await getDbConnection(); logger.debug(`${FILE_NAME}: Connected to database for rule lookup: ${ruleId}`); // Get the base rule using promisified method const rule = await db.getAsync('SELECT * FROM sigma_rules WHERE id = ?', [ruleId]); if (!rule) { logger.warn(`${FILE_NAME}: Rule with ID ${ruleId} not found in database`); return null; } logger.debug(`${FILE_NAME}: Found base rule with ID ${ruleId}, content length: ${rule.content ? rule.content.length : 0}`); // Get parameters using promisified method const paramsAsync = await db.allAsync('SELECT param_name, param_value, param_type FROM rule_parameters WHERE rule_id = ?', [ruleId]); logger.debug(`${FILE_NAME}: Params query returned ${paramsAsync ? paramsAsync.length : 0} results via allAsync`); // Check if content is missing if (!rule.content) { logger.warn(`${FILE_NAME}: Rule with ID ${ruleId} exists but has no content`); rule.content_missing = true; } // Get all parameters for this rule with case-insensitive matching try { const params = await new Promise((resolve, reject) => { db.all( 'SELECT param_name, param_value, param_type FROM rule_parameters WHERE LOWER(rule_id) = LOWER(?)', [ruleId], (err, rows) => { if (err) reject(err); else resolve(rows); } ); }); logger.debug(`${FILE_NAME}: Retrieved ${params ? params.length : 0} parameters for rule ${ruleId}`); // Validate params is an array if (params && Array.isArray(params)) { // Attach parameters to the rule object rule.parameters = {}; for (const param of params) { if (param && param.param_name) { // Convert value based on type let value = param.param_value; if (param.param_type === 'object' || param.param_type === 'array') { try { value = JSON.parse(param.param_value); } catch (parseError) { logger.warn(`${FILE_NAME}: Failed to parse JSON for parameter ${param.param_name}: ${parseError.message}`); } } else if (param.param_type === 'boolean') { value = param.param_value === 'true'; } else if (param.param_type === 'number') { value = Number(param.param_value); } rule.parameters[param.param_name] = value; } } logger.debug(`${FILE_NAME}: Successfully processed ${Object.keys(rule.parameters).length} parameters for rule ${ruleId}`); } else { logger.warn(`${FILE_NAME}: Parameters for rule ${ruleId} not available or not iterable`); rule.parameters = {}; } } catch (paramError) { logger.error(`${FILE_NAME}: Error fetching parameters for rule ${ruleId}: ${paramError.message}`); logger.debug(`${FILE_NAME}: Parameter error stack: ${paramError.stack}`); rule.parameters = {}; } return rule; } catch (error) { logger.error(`${FILE_NAME}: Error finding rule ${ruleId}: ${error.message}`); logger.debug(`${FILE_NAME}: Error stack: ${error.stack}`); return null; } finally { // Close the database connection if it was opened if (db && typeof db.close === 'function') { try { await db.close(); logger.debug(`${FILE_NAME}: Database connection closed after rule lookup`); } catch (closeError) { logger.warn(`${FILE_NAME}: Error closing database connection: ${closeError.message}`); } } } } module.exports = { getAllRuleIds, findRuleById };