mod disruptive;
mod flow;
mod data;
mod metadata;
pub use disruptive::*;
pub use flow::*;
pub use data::*;
pub use metadata::*;
use crate::parser::{Action, DisruptiveAction, FlowAction, DataAction, MetadataAction, LoggingAction, SetVarValue};
#[derive(Debug, Clone)]
pub struct ActionResult {
pub disruptive: Option<DisruptiveOutcome>,
pub flow: FlowOutcome,
pub setvar_ops: Vec<SetVarOp>,
pub captures: Vec<String>,
pub metadata: RuleMetadata,
}
impl Default for ActionResult {
fn default() -> Self {
Self {
disruptive: None,
flow: FlowOutcome::Continue,
setvar_ops: Vec::new(),
captures: Vec::new(),
metadata: RuleMetadata::default(),
}
}
}
#[derive(Debug, Clone)]
pub enum DisruptiveOutcome {
Deny(u16),
Block,
Allow,
Redirect(String),
Pass,
Drop,
}
#[derive(Debug, Clone, PartialEq)]
pub enum FlowOutcome {
Continue,
Chain,
Skip(u32),
SkipAfter(String),
}
#[derive(Debug, Clone)]
pub struct SetVarOp {
pub collection: String,
pub name: String,
pub operation: SetVarOperation,
}
#[derive(Debug, Clone)]
pub enum SetVarOperation {
Set(String),
Increment(i64),
Decrement(i64),
Delete,
}
#[derive(Debug, Clone, Default)]
pub struct RuleMetadata {
pub id: Option<String>,
pub msg: Option<String>,
pub logdata: Option<String>,
pub severity: Option<u8>,
pub tags: Vec<String>,
pub maturity: Option<u8>,
pub accuracy: Option<u8>,
pub rev: Option<String>,
pub ver: Option<String>,
}
pub fn execute_actions(
actions: &[Action],
matched_value: Option<&str>,
captures: &[String],
) -> ActionResult {
let mut result = ActionResult::default();
result.captures = captures.to_vec();
for action in actions {
match action {
Action::Disruptive(d) => {
result.disruptive = Some(execute_disruptive(d));
}
Action::Flow(f) => {
result.flow = execute_flow(f);
}
Action::Data(d) => {
execute_data(d, &mut result, matched_value);
}
Action::Metadata(m) => {
execute_metadata(m, &mut result.metadata);
}
Action::Logging(l) => {
execute_logging(l, &mut result.metadata);
}
Action::Control(_) => {
}
Action::Transformation(_) => {
}
}
}
result
}
fn execute_disruptive(action: &DisruptiveAction) -> DisruptiveOutcome {
match action {
DisruptiveAction::Deny => DisruptiveOutcome::Deny(403),
DisruptiveAction::Block => DisruptiveOutcome::Block,
DisruptiveAction::Allow | DisruptiveAction::AllowPhase | DisruptiveAction::AllowRequest => {
DisruptiveOutcome::Allow
}
DisruptiveAction::Pass => DisruptiveOutcome::Pass,
DisruptiveAction::Drop => DisruptiveOutcome::Drop,
DisruptiveAction::Redirect(url) => DisruptiveOutcome::Redirect(url.clone()),
}
}
fn execute_flow(action: &FlowAction) -> FlowOutcome {
match action {
FlowAction::Chain => FlowOutcome::Chain,
FlowAction::Skip(n) => FlowOutcome::Skip(*n),
FlowAction::SkipAfter(marker) => FlowOutcome::SkipAfter(marker.clone()),
FlowAction::MultiMatch => FlowOutcome::Continue, }
}
fn execute_data(action: &DataAction, result: &mut ActionResult, _matched_value: Option<&str>) {
match action {
DataAction::Capture => {
}
DataAction::SetVar(spec) => {
let op = match &spec.value {
SetVarValue::String(v) => SetVarOperation::Set(v.clone()),
SetVarValue::Int(v) => SetVarOperation::Set(v.to_string()),
SetVarValue::Increment(v) => SetVarOperation::Increment(*v),
SetVarValue::Decrement(v) => SetVarOperation::Decrement(*v),
SetVarValue::Delete => SetVarOperation::Delete,
};
result.setvar_ops.push(SetVarOp {
collection: spec.collection.clone(),
name: spec.key.clone(),
operation: op,
});
}
DataAction::InitCol { .. } => {
}
DataAction::SetUid(_) => {
}
DataAction::SetSid(_) => {
}
DataAction::ExpireVar { .. } => {
}
DataAction::DeprecateVar(_) => {
}
DataAction::Exec(_) => {
}
DataAction::Prepend(_) | DataAction::Append(_) => {
}
}
}
fn execute_metadata(action: &MetadataAction, metadata: &mut RuleMetadata) {
match action {
MetadataAction::Id(id) => {
metadata.id = Some(id.to_string());
}
MetadataAction::Phase(_) => {
}
MetadataAction::Msg(msg) => {
metadata.msg = Some(msg.clone());
}
MetadataAction::Severity(sev) => {
metadata.severity = Some(*sev);
}
MetadataAction::Tag(tag) => {
metadata.tags.push(tag.clone());
}
MetadataAction::Maturity(m) => {
metadata.maturity = Some(*m);
}
MetadataAction::Accuracy(a) => {
metadata.accuracy = Some(*a);
}
MetadataAction::Rev(rev) => {
metadata.rev = Some(rev.clone());
}
MetadataAction::Ver(ver) => {
metadata.ver = Some(ver.clone());
}
MetadataAction::LogData(data) => {
metadata.logdata = Some(data.clone());
}
MetadataAction::Status(_) => {
}
}
}
fn execute_logging(action: &LoggingAction, _metadata: &mut RuleMetadata) {
match action {
LoggingAction::Log | LoggingAction::NoLog | LoggingAction::AuditLog | LoggingAction::NoAuditLog => {
}
LoggingAction::SanitiseMatched | LoggingAction::SanitizeMatched => {
}
LoggingAction::SanitiseArg(_)
| LoggingAction::SanitiseRequestHeader(_)
| LoggingAction::SanitiseResponseHeader(_) => {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::{Action, DisruptiveAction, MetadataAction};
#[test]
fn test_execute_deny() {
let actions = vec![Action::Disruptive(DisruptiveAction::Deny)];
let result = execute_actions(&actions, None, &[]);
assert!(matches!(result.disruptive, Some(DisruptiveOutcome::Deny(403))));
}
#[test]
fn test_execute_metadata() {
let actions = vec![
Action::Metadata(MetadataAction::Id(12345)),
Action::Metadata(MetadataAction::Msg("Test rule".to_string())),
Action::Metadata(MetadataAction::Severity(2)),
Action::Metadata(MetadataAction::Tag("attack-sqli".to_string())),
];
let result = execute_actions(&actions, None, &[]);
assert_eq!(result.metadata.id, Some("12345".to_string()));
assert_eq!(result.metadata.msg, Some("Test rule".to_string()));
assert_eq!(result.metadata.severity, Some(2));
assert_eq!(result.metadata.tags, vec!["attack-sqli".to_string()]);
}
}