fylgja/src/sigma_db/queries/rule-retrieval.js
2025-04-18 15:45:26 -04:00

164 lines
No EOL
5.6 KiB
JavaScript

/**
* 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>} 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<Object|null>} 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
};