santh-sigma 0.1.4

Sigma rule compiler for security research
Documentation
use super::*;

/// Single-shot glob matcher used only by tests to cross-check the pre-lowered
/// production path. Mirrors the removed `wildcard_matches` wrapper.
fn wildcard_matches(pattern: &str, candidate: &str) -> bool {
    if !pattern.contains('*') {
        return pattern.eq_ignore_ascii_case(candidate);
    }
    let pattern_lc = pattern.to_ascii_lowercase();
    wildcard_matches_lowered(&pattern_lc, candidate)
}

#[test]
fn flatten_1_of_them_is_or() {
    let expr = parse_condition("1 of them").unwrap();
    let names = vec!["sel1".into(), "sel2".into()];
    let (cond, terms) = flatten_condition(&expr, &names).unwrap();
    assert_eq!(cond, MatcherCondition::Or);
    assert_eq!(terms.len(), 2);
    assert!(terms.iter().all(|t| !t.negative));
}

#[test]
fn flatten_all_of_them_is_and() {
    let expr = parse_condition("all of them").unwrap();
    let names = vec!["sel1".into(), "sel2".into()];
    let (cond, terms) = flatten_condition(&expr, &names).unwrap();
    assert_eq!(cond, MatcherCondition::And);
    assert_eq!(terms.len(), 2);
}

#[test]
fn flatten_1_of_pattern_selects_matching_selections() {
    let expr = parse_condition("1 of sel_a*").unwrap();
    let names = vec!["sel_a1".into(), "sel_a2".into(), "sel_b".into()];
    let (cond, terms) = flatten_condition(&expr, &names).unwrap();
    assert_eq!(cond, MatcherCondition::Or);
    assert_eq!(terms.len(), 2);
    assert!(terms.iter().any(|t| t.name == "sel_a1"));
    assert!(terms.iter().any(|t| t.name == "sel_a2"));
}

#[test]
fn flatten_all_of_pattern_selects_matching_selections() {
    let expr = parse_condition("all of sel_*").unwrap();
    let names = vec!["sel_1".into(), "sel_2".into(), "other".into()];
    let (cond, terms) = flatten_condition(&expr, &names).unwrap();
    assert_eq!(cond, MatcherCondition::And);
    assert_eq!(terms.len(), 2);
}

#[test]
fn resolve_wildcard_pattern_is_case_insensitive_after_hoisting_lowercase() {
    // The pattern is uppercased and lowercased once (not per candidate);
    // it must still match candidates case-insensitively and select exactly
    // the matching ones, sorted.
    let names = vec![
        "Sel_Alpha".to_string(),
        "SEL_beta".to_string(),
        "other".to_string(),
    ];
    let got = resolve_selection_pattern("SEL_*", &names).unwrap();
    assert_eq!(got, vec!["SEL_beta".to_string(), "Sel_Alpha".to_string()]);
}

#[test]
fn wildcard_matches_lowered_equals_wildcard_matches() {
    // The hoisted pre-lowered path must be behavior-identical to the
    // single-shot entry point for mixed-case pattern/candidate pairs.
    let cases = [
        ("A*C", "abc"),
        ("A*C", "ABDC"),
        ("*mid*", "XmidY"),
        ("pre*", "PREfix"),
        ("A*C", "xyz"),
        ("*end", "the_END"),
    ];
    for (pat, cand) in cases {
        let via_entry = wildcard_matches(pat, cand);
        let via_lowered = wildcard_matches_lowered(&pat.to_ascii_lowercase(), cand);
        assert_eq!(via_entry, via_lowered, "mismatch for {pat} vs {cand}");
    }
}
#[test]
fn test_wildcard_matches_lowered_anchoring() {
    assert!(wildcard_matches_lowered("a*a", "a_a_x_a"));
    assert!(wildcard_matches_lowered("a*b*a", "a_b_a_b_a"));
    assert!(!wildcard_matches_lowered("a*b*a", "a_b_a_b"));
    assert!(wildcard_matches_lowered("sel_*_rule", "sel_a_x_rule"));
    assert!(!wildcard_matches_lowered("sel_*_rule", "sel_a_x_rule_extra"));
}

#[test]
fn flatten_near_is_rejected() {
    let expr = parse_condition("sel1 | near sel2").unwrap();
    let names = vec!["sel1".into(), "sel2".into()];
    let result = flatten_condition(&expr, &names);
    assert!(result.is_err(), "near must be rejected during flattening");
    let err = result.unwrap_err().to_string();
    assert!(err.contains("near"));
}

#[test]
fn flatten_and_condition_is_and() {
    let expr = parse_condition("sel1 and sel2").unwrap();
    let names = vec!["sel1".into(), "sel2".into()];
    let (cond, terms) = flatten_condition(&expr, &names).unwrap();
    assert_eq!(cond, MatcherCondition::And);
    assert_eq!(terms.len(), 2);
}

#[test]
fn flatten_or_condition_is_or() {
    let expr = parse_condition("sel1 or sel2").unwrap();
    let names = vec!["sel1".into(), "sel2".into()];
    let (cond, terms) = flatten_condition(&expr, &names).unwrap();
    assert_eq!(cond, MatcherCondition::Or);
    assert_eq!(terms.len(), 2);
}