]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/integers: count argument for multi-integers
authorPhilippe Antoine <pantoine@oisf.net>
Wed, 24 Sep 2025 12:47:20 +0000 (14:47 +0200)
committerVictor Julien <victor@inliniac.net>
Thu, 16 Oct 2025 19:33:29 +0000 (21:33 +0200)
Ticket: 7211

Allows to count the number of elements, without matching on
individual elements

doc/userguide/rules/integer-keywords.rst
rust/src/detect/uint.rs

index cd59a44ff630cae32ab3a0780cbdf98ce88edb24..81492ca4cd7dd25cdaf87c233f8f8635c45b3341 100644 (file)
@@ -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;
index b4cc2435745ba27592a6ca86bd8ed6a2cf86968a..e7ded334f354992721349596cf649b0c7bc979bb 100644 (file)
@@ -59,6 +59,7 @@ pub enum DetectUintIndex {
     OrAbsent,
     Index((bool, i32)),
     NumberMatches(DetectUintData<u32>),
+    Count(DetectUintData<u32>),
 }
 
 #[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<u32>> {
+    let (s, _) = tag("count")(s)?;
+    let (s, _) = opt(is_a(" "))(s)?;
+    let (s, du32) = detect_parse_uint::<u32>(s)?;
+    Ok((s, du32))
+}
+
 fn parse_uint_index(parts: &[&str]) -> Option<DetectUintIndex> {
     let index = if parts.len() >= 2 {
         match parts[1] {
@@ -122,6 +130,8 @@ fn parse_uint_index(parts: &[&str]) -> Option<DetectUintIndex> {
             // 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<T: DetectIntType>(s: &str) -> Option<Detec
     }
 
     let index = parse_uint_index(&parts)?;
+    if let DetectUintIndex::Count(_) = &index {
+        return Some(DetectUintArrayData {
+            du: DetectUintData::<T> {
+                arg1: T::min_value(),
+                arg2: T::min_value(),
+                mode: DetectUintMode::DetectUintModeEqual,
+            },
+            index,
+            start: 0,
+            end: 0,
+        });
+    }
+
     let (_, du) = detect_parse_uint::<T>(parts[0]).ok()?;
     let (start, end) = parse_uint_subslice(&parts)?;
 
@@ -157,6 +180,19 @@ pub(crate) fn detect_parse_array_uint_enum<T1: DetectIntType, T2: EnumString<T1>
     }
 
     let index = parse_uint_index(&parts)?;
+    if let DetectUintIndex::Count(_) = &index {
+        return Some(DetectUintArrayData {
+            du: DetectUintData::<T1> {
+                arg1: T1::min_value(),
+                arg2: T1::min_value(),
+                mode: DetectUintMode::DetectUintModeEqual,
+            },
+            index,
+            start: 0,
+            end: 0,
+        });
+    }
+
     let du = detect_parse_uint_enum::<T1, T2>(parts[0])?;
     let (start, end) = parse_uint_subslice(&parts)?;
 
@@ -234,6 +270,26 @@ pub(crate) fn detect_uint_match_at_index<T, U: DetectIntType>(
             }
             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;