From 5d898ebeda11958896c960c0f10712ccf6af381e Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Thu, 2 Nov 2023 15:26:45 +0100 Subject: [PATCH] new: implemented support for integer range expression --- src/creds/expression.rs | 80 ++++++++++++++++++++++++++++ src/creds/iterator/mod.rs | 5 ++ src/creds/iterator/range.rs | 103 ++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 src/creds/iterator/range.rs diff --git a/src/creds/expression.rs b/src/creds/expression.rs index 207b75b..ea32908 100644 --- a/src/creds/expression.rs +++ b/src/creds/expression.rs @@ -10,6 +10,8 @@ const DEFAULT_PERMUTATIONS_CHARSET: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJ lazy_static! { static ref PERMUTATIONS_PARSER: Regex = Regex::new(r"^#(\d+)-(\d+)(:.+)?$").unwrap(); + static ref RANGE_MIN_MAX_PARSER: Regex = Regex::new(r"^\[(\d+)-(\d+)\]$").unwrap(); + static ref RANGE_SET_PARSER: Regex = Regex::new(r"^\[(\d+(,\s*\d+)*)?\]$").unwrap(); } #[derive(Clone, Debug, PartialEq)] @@ -25,6 +27,11 @@ pub(crate) enum Expression { max: usize, charset: String, }, + Range { + min: usize, + max: usize, + set: Vec, + }, Glob { pattern: String, }, @@ -53,6 +60,13 @@ impl fmt::Display for Expression { ) } Expression::Glob { pattern } => write!(f, "glob {}", pattern), + Expression::Range { min, max, set } => { + if set.is_empty() { + write!(f, "range {} -> {}", min, max) + } else { + write!(f, "range {:?}", set) + } + } } } } @@ -107,6 +121,35 @@ pub(crate) fn parse_expression(expr: Option<&String>) -> Expression { } }; } + // range expression or constant + '[' => { + return if let Some(captures) = RANGE_MIN_MAX_PARSER.captures(expr) { + // [min-max] + Expression::Range { + min: captures.get(1).unwrap().as_str().parse().unwrap(), + max: captures.get(2).unwrap().as_str().parse().unwrap(), + set: vec![], + } + } else if let Some(captures) = RANGE_SET_PARSER.captures(expr) { + // [n, n, n, ...] + Expression::Range { + min: 0, + max: 0, + set: captures + .get(1) + .unwrap() + .as_str() + .split(',') + .map(|s| s.trim().parse().unwrap()) + .collect(), + } + } else { + // constant value casually starting with [ + Expression::Constant { + value: expr.to_owned(), + } + }; + } // file name or constant _ => { let filepath = Path::new(&expr); @@ -182,6 +225,17 @@ mod tests { ) } + #[test] + fn can_parse_constant_with_bracket() { + let res = parse_expression(Some("[m_n0t_@_range]".to_owned()).as_ref()); + assert_eq!( + res, + Expression::Constant { + value: "[m_n0t_@_range]".to_owned() + } + ) + } + #[test] fn can_parse_permutations_with_default_charset() { let res = parse_expression(Some("#1-3".to_owned()).as_ref()); @@ -208,6 +262,32 @@ mod tests { ) } + #[test] + fn can_parse_range_with_min_max() { + let res = parse_expression(Some("[1-3]".to_owned()).as_ref()); + assert_eq!( + res, + Expression::Range { + min: 1, + max: 3, + set: vec![], + } + ) + } + + #[test] + fn can_parse_range_with_set() { + let res = parse_expression(Some("[1,3,4, 5, 6, 7, 8, 12,666]".to_owned()).as_ref()); + assert_eq!( + res, + Expression::Range { + min: 0, + max: 0, + set: vec![1, 3, 4, 5, 6, 7, 8, 12, 666], + } + ) + } + #[test] fn can_parse_glob() { let res = parse_expression(Some("@/etc/*".to_owned()).as_ref()); diff --git a/src/creds/iterator/mod.rs b/src/creds/iterator/mod.rs index 11ac4cd..733bb53 100644 --- a/src/creds/iterator/mod.rs +++ b/src/creds/iterator/mod.rs @@ -6,6 +6,7 @@ mod empty; mod glob; mod permutations; mod permutator; +mod range; mod wordlist; pub(crate) trait Iterator: std::iter::Iterator { @@ -30,6 +31,10 @@ pub(crate) fn new(expr: Expression) -> Result, Error> { let it = glob::Glob::new(pattern)?; Ok(Box::new(it)) } + Expression::Range { min, max, set } => { + let it = range::Range::new(min, max, set)?; + Ok(Box::new(it)) + } } } diff --git a/src/creds/iterator/range.rs b/src/creds/iterator/range.rs new file mode 100644 index 0000000..d41a0c5 --- /dev/null +++ b/src/creds/iterator/range.rs @@ -0,0 +1,103 @@ +use crate::{creds, session::Error}; + +pub(crate) struct Range { + max: usize, + set: Vec, + current: usize, + elements: usize, +} + +impl Range { + pub fn new(min: usize, max: usize, set: Vec) -> Result { + if set.is_empty() { + if min > max { + return Err( + "left side of range expression can't be greater than the right side".to_owned(), + ); + } + + let elements = max - min + 1; + Ok(Self { + max, + current: min, + elements, + set: vec![], + }) + } else { + let elements = set.len(); + Ok(Self { + max: 0, + current: 0, + set, + elements, + }) + } + } +} + +impl creds::Iterator for Range { + fn search_space_size(&self) -> usize { + self.elements + } +} + +impl std::iter::Iterator for Range { + type Item = String; + + fn next(&mut self) -> Option { + if self.set.is_empty() { + return if self.current <= self.max { + let ret = self.current; + self.current += 1; + Some(ret.to_string()) + } else { + None + }; + } else { + return if self.current < self.elements { + let ret = self.set[self.current]; + self.current += 1; + Some(ret.to_string()) + } else { + None + }; + } + } +} + +#[cfg(test)] +mod tests { + use crate::creds::{iterator, Expression}; + + #[test] + fn can_handle_min_max_range() { + let expected = vec!["1", "2", "3", "4", "5"]; + let gen = iterator::new(Expression::Range { + min: 1, + max: 5, + set: vec![], + }) + .unwrap(); + let tot = gen.search_space_size(); + let vec: Vec = gen.collect(); + + assert_eq!(tot, expected.len()); + assert_eq!(vec, expected); + } + + #[test] + fn can_handle_set_range() { + let expected = vec!["1", "666", "2", "234", "5", "19"]; + let gen = iterator::new(Expression::Range { + min: 0, + max: 0, + set: vec![1, 666, 2, 234, 5, 19], + }) + .unwrap(); + let tot = gen.search_space_size(); + let vec: Vec = gen.collect(); + + assert_eq!(tot, expected.len()); + assert_eq!(vec, expected); + } +}