@flamesshield/rules-engine
Version:
Independent rules engine for security analysis of Firebase
124 lines • 5.46 kB
JavaScript
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