UNPKG

@flamesshield/rules-engine

Version:

Independent rules engine for security analysis of Firebase

124 lines 5.46 kB
import { Engine } from 'json-rules-engine'; import { enrichProjectInformation } from '../utils/computed-properties.js'; export { enrichProjectInformation } from '../utils/computed-properties.js'; /** * Execute rules against project information */ export async function executeRules(context) { const { project_information, configured_rules } = context; if (configured_rules.length === 0) { console.log(`No rules to execute for project ${project_information.project_id}`); return []; } // Enrich project information with computed properties const enrichedProjectInfo = await enrichProjectInformation(project_information); // Use enriched project information directly as facts const facts = enrichedProjectInfo; console.log(`Executing ${configured_rules.length} rules against project ${project_information.project_id}`); // Create rule engine const engine = new Engine(); // Add rules to engine for (const rule of configured_rules) { try { engine.addRule({ conditions: rule.conditions, event: rule.event, priority: getSeverityPriority(rule.severity ?? "low"), name: rule.name }); } catch (error) { console.error(`Error adding rule ${rule.id} to engine:`, error); } } // Execute rules const results = []; console.log(`Facts to evaluate:`, JSON.stringify(facts, null, 2)); console.log(`Configured rules:`, JSON.stringify(configured_rules, null, 2)); try { const engineResults = await engine.run(facts); // Serialize the facts to prevent any serialization issues const serializedFacts = JSON.parse(JSON.stringify(facts)); console.log(`Rule execution completed: ${engineResults.events.length} events triggered`); // Process triggered rules for (const event of engineResults.events) { let triggeredRule; // Prefer matching by rule_id in event params if present if (event.params?.rule_id && typeof event.params.rule_id === 'string') { triggeredRule = configured_rules.find(rule => rule.id === event.params?.rule_id); } // Fallback: match by event type and message if no rule_id if (!triggeredRule) { triggeredRule = configured_rules.find(rule => rule.event.type === event.type && rule.event.params && event.params && rule.event.params.message === event.params.message); } // Fallback: match by event type only (legacy, not recommended) if (!triggeredRule) { triggeredRule = configured_rules.find(rule => rule.event.type === event.type); } if (triggeredRule) { results.push({ rule_id: triggeredRule.id, rule_name: triggeredRule.name, rule: triggeredRule, // Add rule object for test compatibility severity: triggeredRule.severity ?? "low", category: triggeredRule.category ?? "general", message: event.params?.message || triggeredRule.description, triggered: true, facts_used: serializedFacts, event_params: JSON.parse(JSON.stringify(event.params || {})) }); } } // Add non-triggered rules to results for completeness console.log(`Before adding non-triggered: ${results.length} results, ${configured_rules.length} total rules`); for (const rule of configured_rules) { const wasTriggered = results.some(result => result.rule_id === rule.id); console.log(`Rule ${rule.id}: was triggered = ${wasTriggered}`); if (!wasTriggered) { console.log(`Adding non-triggered rule: ${rule.id}`); results.push({ rule_id: rule.id, rule_name: rule.name, rule: rule, // Add rule object for test compatibility severity: rule.severity ?? "low", category: rule.category || "general", message: rule.description, triggered: false, facts_used: {} // Empty object instead of null }); } } console.log(`After adding non-triggered: ${results.length} results`); const triggeredCount = results.filter(r => r.triggered).length; console.log(`Rule execution completed: ${triggeredCount}/${configured_rules.length} rules triggered`); } catch (error) { console.error('Error executing rules:', error); throw error; } return results; } /** * Get priority value based on severity (higher number = higher priority) */ function getSeverityPriority(severity) { switch (severity.toLowerCase()) { case 'critical': return 100; case 'high': return 75; case 'medium': return 50; case 'low': return 25; default: return 25; } } /** * Create a rule execution context */ export function createRuleExecutionContext(project_information, configured_rules) { return { project_information, configured_rules, execution_timestamp: new Date().toISOString() }; } //# sourceMappingURL=index.js.map