use crate::ast::SelectorPattern;
pub fn detection_name_matches(pattern: &str, name: &str) -> bool {
if pattern == "*" {
return true;
}
if let Some(prefix) = pattern.strip_suffix('*') {
return name.starts_with(prefix);
}
if let Some(suffix) = pattern.strip_prefix('*') {
return name.ends_with(suffix);
}
if let Some((prefix, suffix)) = pattern.split_once('*') {
return name.len() >= prefix.len() + suffix.len()
&& name.starts_with(prefix)
&& name.ends_with(suffix);
}
pattern == name
}
impl SelectorPattern {
pub fn matches_detection_name(&self, name: &str) -> bool {
match self {
SelectorPattern::Them => !name.starts_with('_'),
SelectorPattern::Pattern(pat) => {
(pat.starts_with('_') || !name.starts_with('_'))
&& detection_name_matches(pat, name)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn star_only_matches_anything() {
assert!(detection_name_matches("*", "anything"));
assert!(detection_name_matches("*", ""));
}
#[test]
fn star_suffix_matches_prefix() {
assert!(detection_name_matches("selection_*", "selection_main"));
assert!(detection_name_matches("selection_*", "selection_"));
assert!(!detection_name_matches("selection_*", "filter_main"));
}
#[test]
fn star_prefix_matches_suffix() {
assert!(detection_name_matches("*_main", "selection_main"));
assert!(!detection_name_matches("*_main", "selection_alt"));
}
#[test]
fn star_middle_matches_prefix_and_suffix() {
assert!(detection_name_matches("sel*main", "selection_main"));
assert!(!detection_name_matches("sel*main", "filter_main"));
assert!(!detection_name_matches("sel*main", "selection_alt"));
}
#[test]
fn star_middle_prefix_and_suffix_do_not_overlap() {
assert!(!detection_name_matches("sel*lection", "selection"));
assert!(detection_name_matches("sel*lection", "sellection"));
assert!(!detection_name_matches("a_*_main", "a_main"));
assert!(detection_name_matches("a_*_main", "a__main"));
}
#[test]
fn exact_match_without_star() {
assert!(detection_name_matches("selection", "selection"));
assert!(!detection_name_matches("selection", "filter"));
assert!(!detection_name_matches("selection", "selection_main"));
}
#[test]
fn underscore_pattern_is_literal() {
assert!(detection_name_matches("_helper", "_helper"));
assert!(!detection_name_matches("_helper", "helper"));
assert!(detection_name_matches("*", "_helper"));
}
#[test]
fn selector_pattern_them_skips_underscore_names() {
let them = SelectorPattern::Them;
assert!(them.matches_detection_name("selection"));
assert!(!them.matches_detection_name("_internal"));
}
#[test]
fn selector_pattern_pattern_uses_glob() {
let pat = SelectorPattern::Pattern("selection_*".to_string());
assert!(pat.matches_detection_name("selection_main"));
assert!(!pat.matches_detection_name("filter_main"));
}
#[test]
fn selector_pattern_skips_underscore_names_unless_it_starts_with_one() {
let star = SelectorPattern::Pattern("*".to_string());
assert!(star.matches_detection_name("selection"));
assert!(!star.matches_detection_name("_internal"));
let suffix = SelectorPattern::Pattern("*_main".to_string());
assert!(!suffix.matches_detection_name("_sel_main"));
let internal = SelectorPattern::Pattern("_internal".to_string());
assert!(internal.matches_detection_name("_internal"));
let hidden = SelectorPattern::Pattern("_*".to_string());
assert!(hidden.matches_detection_name("_internal"));
assert!(!hidden.matches_detection_name("selection"));
}
}