use std::collections::HashMap;
use rsigma_eval::pipeline::PipelineState;
use rsigma_ir::{IrCondition, IrDetection, IrRule};
use rsigma_parser::{Quantifier, SigmaRule};
use crate::backend::Backend;
use crate::error::{ConvertError, Result};
use crate::ir_convert::{Operand, condition_op, detection_op, join, negate, selected_detections};
use crate::state::ConversionState;
pub fn convert_ir_condition(
backend: &dyn Backend,
expr: &IrCondition,
detections: &HashMap<String, IrDetection>,
state: &mut ConversionState,
) -> Result<String> {
match expr {
IrCondition::Detection(name) => {
let det = detections.get(name).ok_or_else(|| {
ConvertError::RuleConversion(format!("detection '{name}' not found"))
})?;
backend.convert_ir_detection(det, state)
}
IrCondition::And(exprs) | IrCondition::Or(exprs) => {
let parts = exprs
.iter()
.map(|e| {
let part = convert_ir_condition(backend, e, detections, state)?;
Ok(Operand::new(part, condition_op(e, detections)))
})
.collect::<Result<Vec<_>>>()?;
join(backend, matches!(expr, IrCondition::And(_)), parts)
}
IrCondition::Not(inner) => {
let part = convert_ir_condition(backend, inner, detections, state)?;
negate(backend, Operand::new(part, condition_op(inner, detections)))
}
IrCondition::Selector {
quantifier,
pattern,
} => {
let names = selected_detections(detections, pattern);
if names.is_empty() {
return Err(ConvertError::RuleConversion(
"selector matched no detections".into(),
));
}
let all = match quantifier {
Quantifier::Any | Quantifier::Count(1) => false,
Quantifier::All => true,
Quantifier::Count(n) => {
return Err(ConvertError::RuleConversion(format!(
"'{n} of' quantifier not supported in conversion"
)));
}
};
let parts = names
.into_iter()
.map(|name| {
let det = &detections[name];
let part = backend.convert_ir_detection(det, state)?;
Ok(Operand::new(part, detection_op(det)))
})
.collect::<Result<Vec<_>>>()?;
join(backend, all, parts)
}
}
}
pub(crate) fn ir_err(e: rsigma_ir::IrError) -> ConvertError {
use rsigma_ir::IrError;
match e {
IrError::InvalidModifiers(m) => ConvertError::UnsupportedModifier(m),
IrError::IncompatibleValue(m) | IrError::ExpectedNumeric(m) => {
ConvertError::UnsupportedValue(m)
}
other => ConvertError::RuleConversion(other.to_string()),
}
}
pub(crate) fn lower_rule_for_conversion(rule: &SigmaRule) -> Result<IrRule> {
let mut ir =
rsigma_ir::lower_rule(rule, &rsigma_ir::LowerOptions::default()).map_err(ir_err)?;
rsigma_ir::encoding::expand_encoded_detections(&mut ir.detections).map_err(ir_err)?;
Ok(ir)
}
pub(crate) fn condition_state(
pipeline_state: &PipelineState,
output_format: &str,
) -> ConversionState {
let mut state = ConversionState::new(pipeline_state.state.clone());
state
.processing_state
.insert("_output_format".to_string(), output_format.into());
state
}
pub fn convert_rule_via_ir(
backend: &dyn Backend,
rule: &SigmaRule,
output_format: &str,
pipeline_state: &PipelineState,
) -> Result<Vec<String>> {
let ir = lower_rule_for_conversion(rule)?;
convert_lowered_rule(backend, rule, &ir, output_format, pipeline_state)
}
pub(crate) fn convert_lowered_rule(
backend: &dyn Backend,
rule: &SigmaRule,
ir: &IrRule,
output_format: &str,
pipeline_state: &PipelineState,
) -> Result<Vec<String>> {
let mut queries = Vec::with_capacity(ir.conditions.len());
for (idx, cond) in ir.conditions.iter().enumerate() {
let mut state = condition_state(pipeline_state, output_format);
let query = convert_ir_condition(backend, cond, &ir.detections, &mut state)?;
let finished = backend.finish_query(rule, query, &state)?;
let finalized = backend.finalize_query(rule, finished, idx, &state, output_format)?;
queries.push(finalized);
}
Ok(queries)
}