Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion engine/src/ast/field_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
Expand Down
41 changes: 40 additions & 1 deletion engine/src/ast/function_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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::<FunctionCallExpr>("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::<crate::FilterValueAst>("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::<FunctionCallExpr>(
"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<LhsValue<'a>> {
match args.next()? {
Ok(LhsValue::Bytes(bytes)) => Some(LhsValue::Int(i64::try_from(bytes.len()).unwrap())),
Expand Down
90 changes: 84 additions & 6 deletions engine/src/ast/logical_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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::<LogicalExpr>("((t))"),
LexErrorKind::NestingLimitExceeded { limit: 1 },
"(t))"
);
}

{
let mut parser = FilterParser::new(scheme);
parser.set_max_nesting_depth(1);
assert_err!(
parser.lex_as::<LogicalExpr>("!!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::<LogicalExpr>("t and (t or t)"),
LexErrorKind::NestingLimitExceeded { limit: 0 },
"(t or t)"
);
}

{
let expr = assert_ok!(
FilterParser::new(scheme).lex_as("not t && f"),
Expand Down
43 changes: 42 additions & 1 deletion engine/src/ast/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
}
}
}
Expand All @@ -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> {
Expand All @@ -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.
Expand All @@ -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<Self, (LexErrorKind, &'i str)> {
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<FilterAst, ParseError<'i>> {
complete(self.lex_as(input.trim())).map_err(|err| ParseError::new(input, err))
Expand Down Expand Up @@ -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
}
}
7 changes: 7 additions & 0 deletions engine/src/lex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading