use super::*;
use secir::template::matchers::MatcherKind;
#[test]
fn title_to_id_basic() {
assert_eq!(
title_to_id("Multiple Failed Logins"),
"multiple-failed-logins"
);
}
#[test]
fn title_to_id_with_special_chars() {
assert_eq!(title_to_id("Deep Nested OR"), "deep-nested-or");
}
#[test]
fn severity_mapping() {
assert_eq!(map_severity("test", "critical").unwrap(), Severity::Critical);
assert_eq!(map_severity("test", "HIGH").unwrap(), Severity::High);
assert_eq!(map_severity("test", "medium").unwrap(), Severity::Medium);
assert_eq!(map_severity("test", "low").unwrap(), Severity::Low);
assert_eq!(map_severity("test", "informational").unwrap(), Severity::Info);
assert_eq!(map_severity("test", "info").unwrap(), Severity::Info);
assert!(
map_severity("test", "unknown").is_err(),
"unknown Sigma level must be rejected"
);
}
#[test]
fn compile_basic_sigma_rule() {
let yaml = r#"
title: Sigma IR Rule
logsource:
product: windows
detection:
selection:
CommandLine|contains: powershell.exe
condition: selection
level: high
"#;
let template = compile_rule(serde_yaml::from_str::<SigmaRule>(yaml).unwrap()).unwrap();
assert_eq!(template.id, "sigma-ir-rule");
assert_eq!(template.info.severity, Severity::High);
assert_eq!(template.protocol, Protocol::File);
assert!(!template.requests.is_empty());
assert!(!template.requests[0].matchers.is_empty());
assert_eq!(
template.requests[0].matchers[0].values,
vec!["powershell.exe"]
);
}
#[test]
fn compile_multiple_values() {
let yaml = r#"
title: Multi Value Rule
detection:
selection:
CommandLine|contains:
- "powershell.exe"
- "cmd.exe"
condition: selection
level: medium
"#;
let template = compile_rule(serde_yaml::from_str::<SigmaRule>(yaml).unwrap()).unwrap();
assert_eq!(template.requests[0].matchers[0].values.len(), 2);
}
#[test]
fn compile_endswith_modifier() {
let yaml = r#"
title: Endswith Rule
detection:
selection:
ParentImage|endswith: "\\winword.exe"
condition: selection
level: low
"#;
let template = compile_rule(serde_yaml::from_str::<SigmaRule>(yaml).unwrap()).unwrap();
assert_eq!(template.requests[0].matchers[0].kind, MatcherKind::Regex);
assert!(template.requests[0].matchers[0].values[0].ends_with('$'));
}
#[test]
fn compile_cased_modifier_drops_case_insensitive_flag() {
let yaml = r#"
title: Cased Rule
detection:
selection:
cmd|cased: "Get-Process"
condition: selection
level: medium
"#;
let template = compile_rule(serde_yaml::from_str::<SigmaRule>(yaml).unwrap()).unwrap();
let value = &template.requests[0].matchers[0].values[0];
assert!(
value.starts_with("(?m)"),
"expected case-sensitive flags, got: {value}"
);
assert!(
!value.starts_with("(?im)"),
"cased must not produce (?im): {value}"
);
assert!(value.contains("Get\\-Process"), "missing literal: {value}");
}
#[test]
fn compile_cased_modifier_on_contains_falls_through_to_regex() {
let yaml = r#"
title: Cased Contains
detection:
selection:
cmd|contains|cased: "PowerShell"
condition: selection
level: medium
"#;
let template = compile_rule(serde_yaml::from_str::<SigmaRule>(yaml).unwrap()).unwrap();
assert_eq!(template.requests[0].matchers[0].kind, MatcherKind::Regex);
let value = &template.requests[0].matchers[0].values[0];
assert!(
value.starts_with("(?m)"),
"expected cased flags, got: {value}"
);
}
#[test]
fn compile_base64_modifier_encodes_value() {
let yaml = r#"
title: Base64 Rule
detection:
selection:
payload|base64: "rm -rf /"
condition: selection
level: critical
"#;
let template = compile_rule(serde_yaml::from_str::<SigmaRule>(yaml).unwrap()).unwrap();
let value = &template.requests[0].matchers[0].values[0];
assert!(
value.contains("cm0gLXJmIC8="),
"expected base64 encoding in matcher value, got: {value}"
);
}
#[test]
fn compile_base64_and_base64offset_are_mutually_exclusive() {
let yaml = r#"
title: Conflicting Modifiers
detection:
selection:
payload|base64|base64offset: "foo"
condition: selection
level: low
"#;
let result = compile_rule(serde_yaml::from_str::<SigmaRule>(yaml).unwrap());
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("mutually exclusive"), "wrong error: {err}");
}
#[test]
fn compile_unknown_modifier_is_rejected_with_actionable_error() {
let yaml = r#"
title: Bad Modifier
detection:
selection:
cmd|wibble: "foo"
condition: selection
level: low
"#;
let result = compile_rule(serde_yaml::from_str::<SigmaRule>(yaml).unwrap());
let err = result.unwrap_err().to_string();
assert!(err.contains("wibble"), "must name bad modifier: {err}");
}
#[test]
fn reserved_detection_keys_are_excluded_from_selections() {
let yaml = r#"
title: Reserved Keys
detection:
condition: selection
timeframe: last_24h
keywords:
- foo
selection:
cmd|contains: evil
"#;
let template = compile_rule(serde_yaml::from_str::<SigmaRule>(yaml).unwrap()).unwrap();
assert_eq!(template.id, "reserved-keys");
assert!(!template.requests.is_empty());
}