164 lines
No EOL
5.6 KiB
JavaScript
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
|
|
}; |