diff --git a/doc/userguide/rules/integer-keywords.rst b/doc/userguide/rules/integer-keywords.rst index cd59a44ff6..81492ca4cd 100644 --- a/doc/userguide/rules/integer-keywords.rst +++ b/doc/userguide/rules/integer-keywords.rst @@ -142,4 +142,17 @@ For the array [1,2,3,4,5,6], here are some examples: * 3:-1 will have subslice [4,5] * -4:4 will have subslice [3,4] -If one index is out of bounds, an empty subslice is used. \ No newline at end of file +If one index is out of bounds, an empty subslice is used. + +Count +----- + +Multi-integer can also just count the number of occurences +without matching to a specific value. + +The syntax is:: + keyword: count [mode] value; + +Examples:: + + http2.window:count >5; diff --git a/rust/src/detect/uint.rs b/rust/src/detect/uint.rs index b4cc243574..e7ded334f3 100644 --- a/rust/src/detect/uint.rs +++ b/rust/src/detect/uint.rs @@ -59,6 +59,7 @@ pub enum DetectUintIndex { OrAbsent, Index((bool, i32)), NumberMatches(DetectUintData), + Count(DetectUintData), } #[derive(Debug, PartialEq)] @@ -112,6 +113,13 @@ fn parse_uint_subslice(parts: &[&str]) -> Option<(i32, i32)> { return Some((start, end)); } +fn parse_uint_count(s: &str) -> IResult<&str, DetectUintData> { + let (s, _) = tag("count")(s)?; + let (s, _) = opt(is_a(" "))(s)?; + let (s, du32) = detect_parse_uint::(s)?; + Ok((s, du32)) +} + fn parse_uint_index(parts: &[&str]) -> Option { let index = if parts.len() >= 2 { match parts[1] { @@ -122,6 +130,8 @@ fn parse_uint_index(parts: &[&str]) -> Option { // not only a literal, but some numeric value _ => return parse_uint_index_val(parts[1]), } + } else if let Ok((_, du)) = parse_uint_count(parts[0]) { + DetectUintIndex::Count(du) } else { DetectUintIndex::Any }; @@ -136,6 +146,19 @@ pub(crate) fn detect_parse_array_uint(s: &str) -> Option { + arg1: T::min_value(), + arg2: T::min_value(), + mode: DetectUintMode::DetectUintModeEqual, + }, + index, + start: 0, + end: 0, + }); + } + let (_, du) = detect_parse_uint::(parts[0]).ok()?; let (start, end) = parse_uint_subslice(&parts)?; @@ -157,6 +180,19 @@ pub(crate) fn detect_parse_array_uint_enum } let index = parse_uint_index(&parts)?; + if let DetectUintIndex::Count(_) = &index { + return Some(DetectUintArrayData { + du: DetectUintData:: { + arg1: T1::min_value(), + arg2: T1::min_value(), + mode: DetectUintMode::DetectUintModeEqual, + }, + index, + start: 0, + end: 0, + }); + } + let du = detect_parse_uint_enum::(parts[0])?; let (start, end) = parse_uint_subslice(&parts)?; @@ -234,6 +270,26 @@ pub(crate) fn detect_uint_match_at_index( } return 0; } + DetectUintIndex::Count(du32) => { + if !eof { + match du32.mode { + DetectUintMode::DetectUintModeGt | DetectUintMode::DetectUintModeGte => {} + _ => { + return 0; + } + } + } + let mut nb = 0u32; + for response in subslice { + if get_value(response).is_some() { + nb += 1; + } + } + if detect_match_uint(du32, nb) { + return 1; + } + return 0; + } DetectUintIndex::All => { if !eof { return 0;