CVE-2022-31099 (Rust, published 2022-06-27T23:15Z)
Description: value: rulex is a new, portable, regular expression language. When parsing untrusted rulex expressions, the stack may overflow, possibly enabling a Denial of Service attack. This happens when parsing an expression with several hundred levels of nesting, causing the process to abort immediately. This is a security concern for you, if your service parses untrusted rulex expressions (expressions provided by an untrusted user), and your service becomes unavailable when the process running rulex aborts due to a stack overflow. The crash is fixed in version **0.4.3**. Affected users are advised to update to this version. There are no known workarounds for this issue.
Severity: MEDIUM (CVSS v3 base score: 6.5)
Weakness: CWE-674 \u2014 Uncontrolled Recursion
The product does not properly control the amount of recursion that takes place,  consuming excessive resources, such as allocated memory or the program stack.
Repository: https://github.com/rulex-rs/rulex
Files changed:
rulex-lib/src/error/parse_error.rs
rulex-lib/src/lib.rs
rulex-lib/src/parse/input.rs
rulex-lib/src/parse/parsers.rs
Commit message: Add recursion limit


Vulnerable code:
        let rule = parse::parse(input)?;
    pub(super) fn from(source: &'i str, tokens: &'b [(Token, Span)]) -> Result<Self, ParseError> {
        Ok(Input { source, tokens })
        Input { source: self.source, tokens: self.tokens }
        Input { source: self.source, tokens }
        (Input { source: self.source, tokens: left }, Input { source: self.source, tokens: right })
pub(crate) fn parse(source: &str) -> Result<Rule<'_>, ParseError> {
    let input = Input::from(source, &tokens)?;
                        cut(parse_or),
            parse_or,
            pair(Token::Not, opt(parse_fixes)),
        map(pair(parse_lookaround, parse_modified), |((kind, span), rule)| {
        pair(opt(parse_capture), tuple((Token::OpenParen, parse_modified, cut(Token::CloseParen)))),

Fixed code:
    #[error("Recursion limit reached. Try a less nested expression")]
    RecursionLimit,

        let rule = parse::parse(input, 256)?;
    recursion: u16,
    pub(super) fn from(
        source: &'i str,
        tokens: &'b [(Token, Span)],
        recursion: u16,
    ) -> Result<Self, ParseError> {
        Ok(Input { source, tokens, recursion })
    }

    pub(super) fn recursion_start(&mut self) -> Result<(), ParseError> {
        self.recursion = self
            .recursion
            .checked_sub(1)
            .ok_or_else(|| ParseErrorKind::RecursionLimit.at(self.span()))?;
        Ok(())
    }

    pub(super) fn recursion_end(&mut self) {
        self.recursion += 1;
        Input { ..*self }
        Input { tokens, ..*self }
        (Input { tokens: left, ..*self }, Input { tokens: right, ..*self })
pub(crate) fn parse(source: &str, recursion: u16) -> Result<Rule<'_>, ParseError> {
    let input = Input::from(source, &tokens, recursion)?;
fn recurse<'i, 'b, O>(
    mut parser: impl Parser<Input<'i, 'b>, O, ParseError>,
) -> impl FnMut(Input<'i, 'b>) -> PResult<'i, 'b, O> {
    move |mut input| {
        input.recursion_start().map_err(nom::Err::Failure)?;

        match parser.parse(input) {
            Ok((mut input, output)) => {
                input.recursion_end();
                Ok((input, output))
            }
            Err(e) => Err(e),
        }
    }
}

                        cut(recurse(parse_or)),
            recurse(parse_or),
            pair(Token::Not, opt(recurse(parse_fixes))),
        map(pair(parse_lookaround, recurse(parse_modified)), |((kind, span), rule)| {
        pair(
            opt(parse_capture),
            tuple((Token::OpenParen, recurse(parse_modified), cut(Token::CloseParen))),
        ),