diff --git a/engine/src/ast/field_expr.rs b/engine/src/ast/field_expr.rs index 2a171a1e..44e4e87a 100644 --- a/engine/src/ast/field_expr.rs +++ b/engine/src/ast/field_expr.rs @@ -255,7 +255,8 @@ impl<'i, 's> LexWith<'i, &FilterParser<'s>> for IdentifierExpr { match item { Identifier::Field(field) => Ok((IdentifierExpr::Field(field.to_owned()), input)), Identifier::Function(function) => { - FunctionCallExpr::lex_with_function(input, parser, function) + let nested_parser = parser.with_increased_nesting(skip_space(input))?; + FunctionCallExpr::lex_with_function(input, &nested_parser, function) .map(|(call, input)| (IdentifierExpr::FunctionCallExpr(call), input)) } } diff --git a/engine/src/ast/function_expr.rs b/engine/src/ast/function_expr.rs index c9808941..ef6deab7 100644 --- a/engine/src/ast/function_expr.rs +++ b/engine/src/ast/function_expr.rs @@ -512,8 +512,9 @@ impl GetType for FunctionCallExpr { impl<'i> LexWith<'i, &FilterParser<'_>> for FunctionCallExpr { fn lex_with(input: &'i str, parser: &FilterParser<'_>) -> LexResult<'i, Self> { let (function, rest) = FunctionRef::lex_with(input, parser.scheme)?; + let nested_parser = parser.with_increased_nesting(skip_space(rest))?; - Self::lex_with_function(rest, parser, function) + Self::lex_with_function(rest, &nested_parser, function) } } @@ -566,6 +567,44 @@ mod tests { args.next()?.ok() } + #[test] + fn test_function_call_nesting_limit() { + let mut parser = FilterParser::new(&SCHEME); + parser.set_max_nesting_depth(2); + + assert_err!( + parser.lex_as::("echo ( echo ( echo ( http.host ) ) )"), + LexErrorKind::NestingLimitExceeded { limit: 2 }, + "( http.host ) ) )" + ); + } + + #[test] + fn test_value_expr_function_call_nesting_limit() { + let mut parser = FilterParser::new(&SCHEME); + parser.set_max_nesting_depth(2); + + assert_err!( + parser.lex_as::("echo ( echo ( echo ( http.host ) ) )"), + LexErrorKind::NestingLimitExceeded { limit: 2 }, + "( http.host ) ) )" + ); + } + + #[test] + fn test_logical_argument_nesting_limit_is_counted_via_function_and_parentheses() { + let mut parser = FilterParser::new(&SCHEME); + parser.set_max_nesting_depth(1); + + assert_err!( + parser.lex_as::( + "any ( ( http.request.headers.is_empty or http.request.headers.is_empty ) )" + ), + LexErrorKind::NestingLimitExceeded { limit: 1 }, + "( http.request.headers.is_empty or http.request.headers.is_empty ) )" + ); + } + fn len_function<'a>(args: FunctionArgs<'_, 'a>) -> Option> { match args.next()? { Ok(LhsValue::Bytes(bytes)) => Some(LhsValue::Int(i64::try_from(bytes.len()).unwrap())), diff --git a/engine/src/ast/logical_expr.rs b/engine/src/ast/logical_expr.rs index 4f1790a0..bb3e60a8 100644 --- a/engine/src/ast/logical_expr.rs +++ b/engine/src/ast/logical_expr.rs @@ -82,18 +82,20 @@ impl LogicalExpr { } fn lex_simple_expr<'i>(input: &'i str, parser: &FilterParser<'_>) -> LexResult<'i, Self> { - Ok(if let Ok(input) = expect(input, "(") { - let input = skip_space(input); - let (expr, input) = LogicalExpr::lex_with(input, parser)?; + Ok(if let Ok(rest) = expect(input, "(") { + let nested_parser = parser.with_increased_nesting(input)?; + let input = skip_space(rest); + let (expr, input) = LogicalExpr::lex_with(input, &nested_parser)?; let input = skip_space(input); let input = expect(input, ")")?; ( LogicalExpr::Parenthesized(Box::new(ParenthesizedExpr { expr })), input, ) - } else if let Ok((op, input)) = UnaryOp::lex(input) { - let input = skip_space(input); - let (arg, input) = Self::lex_simple_expr(input, parser)?; + } else if let Ok((op, rest)) = UnaryOp::lex(input) { + let nested_parser = parser.with_increased_nesting(input)?; + let input = skip_space(rest); + let (arg, input) = Self::lex_simple_expr(input, &nested_parser)?; ( LogicalExpr::Unary { op, @@ -550,6 +552,46 @@ fn test() { } ); + assert_ok!( + FilterParser::new(scheme).lex_as("t and (t or t)"), + LogicalExpr::Combining { + op: LogicalOp::And, + items: vec![ + t_expr(), + LogicalExpr::Parenthesized(Box::new(ParenthesizedExpr { + expr: LogicalExpr::Combining { + op: LogicalOp::Or, + items: vec![t_expr(), t_expr()], + }, + })), + ], + } + ); + + assert_ok!( + FilterParser::new(scheme).lex_as("t and (t or (t and t))"), + LogicalExpr::Combining { + op: LogicalOp::And, + items: vec![ + t_expr(), + LogicalExpr::Parenthesized(Box::new(ParenthesizedExpr { + expr: LogicalExpr::Combining { + op: LogicalOp::Or, + items: vec![ + t_expr(), + LogicalExpr::Parenthesized(Box::new(ParenthesizedExpr { + expr: LogicalExpr::Combining { + op: LogicalOp::And, + items: vec![t_expr(), t_expr()], + }, + })), + ], + }, + })), + ], + } + ); + { let expr = assert_ok!( FilterParser::new(scheme).lex_as("at and af"), @@ -879,6 +921,42 @@ fn test() { not_expr(parenthesized_expr(not_expr(not_expr(at_expr())))) ); + { + let mut parser = FilterParser::new(scheme); + parser.set_max_nesting_depth(1); + assert_err!( + parser.lex_as::("((t))"), + LexErrorKind::NestingLimitExceeded { limit: 1 }, + "(t))" + ); + } + + { + let mut parser = FilterParser::new(scheme); + parser.set_max_nesting_depth(1); + assert_err!( + parser.lex_as::("!!t"), + LexErrorKind::NestingLimitExceeded { limit: 1 }, + "!t" + ); + } + + { + let mut parser = FilterParser::new(scheme); + parser.set_max_nesting_depth(2); + assert_ok!(parser.lex_as("!!t"), not_expr(not_expr(t_expr()))); + } + + { + let mut parser = FilterParser::new(scheme); + parser.set_max_nesting_depth(0); + assert_err!( + parser.lex_as::("t and (t or t)"), + LexErrorKind::NestingLimitExceeded { limit: 0 }, + "(t or t)" + ); + } + { let expr = assert_ok!( FilterParser::new(scheme).lex_as("not t && f"), diff --git a/engine/src/ast/parse.rs b/engine/src/ast/parse.rs index 562703d5..a9b79ffc 100644 --- a/engine/src/ast/parse.rs +++ b/engine/src/ast/parse.rs @@ -106,6 +106,9 @@ pub struct ParserSettings { /// Maximum number of star metacharacters allowed in a wildcard. /// Default: unlimited pub wildcard_star_limit: usize, + /// Maximum nesting depth allowed while parsing. + /// Default: 128 + pub max_nesting_depth: u16, } impl Default for ParserSettings { @@ -117,6 +120,7 @@ impl Default for ParserSettings { // Default value extracted from the regex crate. regex_dfa_size_limit: 2 * (1 << 20), wildcard_star_limit: usize::MAX, + max_nesting_depth: 128, } } } @@ -126,6 +130,7 @@ impl Default for ParserSettings { pub struct FilterParser<'s> { pub(crate) scheme: &'s Scheme, pub(crate) settings: ParserSettings, + current_nesting_depth: u16, } impl<'s> FilterParser<'s> { @@ -135,13 +140,18 @@ impl<'s> FilterParser<'s> { Self { scheme, settings: ParserSettings::default(), + current_nesting_depth: 0, } } /// Creates a new parser with the specified settings. #[inline] pub fn with_settings(scheme: &'s Scheme, settings: ParserSettings) -> Self { - Self { scheme, settings } + Self { + scheme, + settings, + current_nesting_depth: 0, + } } /// Returns the [`Scheme`](struct@Scheme) for which this parser has been constructor for. @@ -158,6 +168,25 @@ impl<'s> FilterParser<'s> { L::lex_with(input, self) } + #[inline] + pub(crate) fn with_increased_nesting<'i>( + &self, + span: &'i str, + ) -> Result { + if self.current_nesting_depth >= self.settings.max_nesting_depth { + Err(( + LexErrorKind::NestingLimitExceeded { + limit: self.settings.max_nesting_depth, + }, + span, + )) + } else { + let mut nested = self.clone(); + nested.current_nesting_depth += 1; + Ok(nested) + } + } + /// Parses a filter expression into an AST form. pub fn parse<'i>(&self, input: &'i str) -> Result> { complete(self.lex_as(input.trim())).map_err(|err| ParseError::new(input, err)) @@ -209,4 +238,16 @@ impl<'s> FilterParser<'s> { pub fn wildcard_get_star_limit(&self) -> usize { self.settings.wildcard_star_limit } + + /// Set the maximum nesting depth allowed while parsing. + #[inline] + pub fn set_max_nesting_depth(&mut self, max_nesting_depth: u16) { + self.settings.max_nesting_depth = max_nesting_depth; + } + + /// Get the maximum nesting depth allowed while parsing. + #[inline] + pub fn max_nesting_depth(&self) -> u16 { + self.settings.max_nesting_depth + } } diff --git a/engine/src/lex.rs b/engine/src/lex.rs index 4fcc5633..d6cdcf0d 100644 --- a/engine/src/lex.rs +++ b/engine/src/lex.rs @@ -150,6 +150,13 @@ pub enum LexErrorKind { /// Name of the list name: String, }, + + /// Maximum nesting depth exceeded while parsing. + #[error("maximum nesting depth exceeded (limit: {limit})")] + NestingLimitExceeded { + /// The configured maximum nesting depth. + limit: u16, + }, } pub type LexError<'i> = (LexErrorKind, &'i str);