From 82f0e725a22a080a526c069255254c5033843b31 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 11 Sep 2025 21:34:53 +0200 Subject: [PATCH] detect/integers: index or_absent and or_oob To match if array is empty, or index is out of bounds --- doc/userguide/rules/integer-keywords.rst | 2 ++ rust/src/detect/uint.rs | 29 +++++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/doc/userguide/rules/integer-keywords.rst b/doc/userguide/rules/integer-keywords.rst index c672dcf464..63f4bd121c 100644 --- a/doc/userguide/rules/integer-keywords.rst +++ b/doc/userguide/rules/integer-keywords.rst @@ -110,8 +110,10 @@ They expand the syntax of a single integer:: all Match only if all indexes match all1 Match only if all and at least one indexes match nb Matches a number of times + or_absent Match with any index or no values 0>= Match specific index 0< Match specific index with back to front indexing + oob_or Match with specific index or index out of bounds ========= ================================================ The index ``all`` will match if there is no value. diff --git a/rust/src/detect/uint.rs b/rust/src/detect/uint.rs index 56109d1828..8b4040b50d 100644 --- a/rust/src/detect/uint.rs +++ b/rust/src/detect/uint.rs @@ -55,7 +55,8 @@ pub enum DetectUintIndex { Any, All, All1, - Index(i32), + OrAbsent, + Index((bool, i32)), NumberMatches(DetectUintData), } @@ -66,12 +67,15 @@ pub struct DetectUintArrayData { } fn parse_uint_index_precise(s: &str) -> IResult<&str, DetectUintIndex> { + let (s, oob) = opt(tag("oob_or"))(s)?; + let (s, _) = opt(is_a(" "))(s)?; let (s, i32_index) = nom_i32(s)?; - Ok((s, DetectUintIndex::Index(i32_index))) + Ok((s, DetectUintIndex::Index((oob.is_some(), i32_index)))) } fn parse_uint_index_nb(s: &str) -> IResult<&str, DetectUintIndex> { let (s, _) = tag("nb")(s)?; + let (s, _) = opt(is_a(" "))(s)?; let (s, du32) = detect_parse_uint::(s)?; Ok((s, DetectUintIndex::NumberMatches(du32))) } @@ -87,6 +91,7 @@ fn parse_uint_index(parts: &[&str]) -> Option { "all" => DetectUintIndex::All, "all1" => DetectUintIndex::All1, "any" => DetectUintIndex::Any, + "or_absent" => DetectUintIndex::OrAbsent, // not only a literal, but some numeric value _ => return parse_uint_index_val(parts[1]), } @@ -136,6 +141,21 @@ pub(crate) fn detect_uint_match_at_index( } return 0; } + DetectUintIndex::OrAbsent => { + let mut has_elem = false; + for response in array { + if let Some(code) = get_value(response) { + if detect_match_uint::(&ctx.du, code) { + return 1; + } + has_elem = true; + } + } + if !has_elem && eof { + return 1; + } + return 0; + } DetectUintIndex::NumberMatches(du32) => { if !eof { match du32.mode { @@ -189,7 +209,7 @@ pub(crate) fn detect_uint_match_at_index( } return 0; } - DetectUintIndex::Index(idx) => { + DetectUintIndex::Index((oob, idx)) => { let index = if *idx < 0 { // negative values for backward indexing. ((array.len() as i32) + idx) as usize @@ -197,6 +217,9 @@ pub(crate) fn detect_uint_match_at_index( *idx as usize }; if array.len() <= index { + if *oob && eof { + return 1; + } return 0; } if let Some(code) = get_value(&array[index]) {