From 59433a2ce164224c6ccf7dd26a6bf2417eca2418 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Mon, 4 Mar 2024 22:14:03 -0500 Subject: [PATCH] feat: move backrefexistscondition into conditional --- crates/redos/src/ilq.rs | 8 ++++++++ crates/redos/src/ir.rs | 34 +++++++++++++++++++--------------- crates/redos/src/lib.rs | 27 ++++++++++++++++++--------- 3 files changed, 45 insertions(+), 24 deletions(-) create mode 100644 crates/redos/src/ilq.rs diff --git a/crates/redos/src/ilq.rs b/crates/redos/src/ilq.rs new file mode 100644 index 0000000..1dbb47b --- /dev/null +++ b/crates/redos/src/ilq.rs @@ -0,0 +1,8 @@ +use crate::ir::Expr; + +/// Returns true iif an ilq is present anywhere in the regex +pub fn scan_ilq(expr: &Expr) -> bool { + match expr { + _ => false, + } +} \ No newline at end of file diff --git a/crates/redos/src/ir.rs b/crates/redos/src/ir.rs index 4d23735..8cbfa74 100644 --- a/crates/redos/src/ir.rs +++ b/crates/redos/src/ir.rs @@ -5,10 +5,14 @@ use fancy_regex::{parse::ExprTree, Assertion, Expr as RegexExpr, LookAround}; use crate::vulnerability::VulnerabilityConfig; +#[derive(Debug, PartialEq, Eq)] +pub enum ExprConditional { + Condition(Box), + BackrefExistsCondition(usize), +} + #[derive(Debug, PartialEq, Eq)] pub enum Expr { - /// An empty expression, e.g. the last branch in `(a|b|)` - Empty, /// Some token, whether its a character class, any character, etc. Token, /// An assertion @@ -38,14 +42,10 @@ pub enum Expr { /// Atomic non-capturing group, e.g. `(?>ab|a)` in text that contains `ab` will match `ab` and /// never backtrack and try `a`, even if matching fails after the atomic group. AtomicGroup(Box), - /// Anchor to match at the position where the previous match ended - ContinueFromPreviousMatchEnd, - /// Conditional expression based on whether the numbered capture group matched or not - BackrefExistsCondition(usize), /// If/Then/Else Condition. If there is no Then/Else, these will just be empty expressions. Conditional { /// The conditional expression to evaluate - condition: Box, + condition: ExprConditional, /// What to execute if the condition is true true_branch: Box, /// What to execute if the condition is false @@ -56,7 +56,7 @@ pub enum Expr { /// Converts a fancy-regex AST to an IR AST pub fn to_expr(tree: &ExprTree, expr: &RegexExpr, config: &VulnerabilityConfig) -> Option { match expr { - RegexExpr::Empty => Some(Expr::Empty), + RegexExpr::Empty => None, RegexExpr::Any { .. } => Some(Expr::Token), RegexExpr::Assertion(a) => Some(Expr::Assertion(*a)), RegexExpr::Literal { .. } => Some(Expr::Token), @@ -109,21 +109,25 @@ pub fn to_expr(tree: &ExprTree, expr: &RegexExpr, config: &VulnerabilityConfig) to_expr(tree, e, config).map(|e| Expr::AtomicGroup(Box::new(e))) } RegexExpr::KeepOut => None, - RegexExpr::ContinueFromPreviousMatchEnd => Some(Expr::ContinueFromPreviousMatchEnd), - RegexExpr::BackrefExistsCondition(i) => Some(Expr::BackrefExistsCondition(*i)), + RegexExpr::ContinueFromPreviousMatchEnd => None, + RegexExpr::BackrefExistsCondition(_) => None, RegexExpr::Conditional { condition, true_branch, false_branch, } => { - let condition = to_expr(tree, condition, config); let true_branch = to_expr(tree, true_branch, config); let false_branch = to_expr(tree, false_branch, config); - if let (Some(condition), Some(true_branch), Some(false_branch)) = - (condition, true_branch, false_branch) + if let (Some(true_branch), Some(false_branch)) = + (true_branch, false_branch) { - Some(Expr::Conditional { - condition: Box::new(condition), + let condition: Option = match condition.as_ref() { + &RegexExpr::BackrefExistsCondition(number) => Some(ExprConditional::BackrefExistsCondition(number)), + expr => to_expr(tree, expr, config).map(|x| ExprConditional::Condition(Box::new(x))) + }; + + condition.map(|condition| Expr::Conditional { + condition, true_branch: Box::new(true_branch), false_branch: Box::new(false_branch), }) diff --git a/crates/redos/src/lib.rs b/crates/redos/src/lib.rs index 149748f..8a9c475 100644 --- a/crates/redos/src/lib.rs +++ b/crates/redos/src/lib.rs @@ -1,9 +1,11 @@ pub mod ir; pub mod vulnerability; +mod ilq; + use fancy_regex::parse::Parser; -use fancy_regex::Result; -use ir::{to_expr, Expr}; +use fancy_regex::{Result, Expr as RegexExpr}; +use ir::{to_expr, Expr, ExprConditional}; use vulnerability::{Vulnerability, VulnerabilityConfig}; /// Returns true iif repeats are present anywhere in the regex @@ -17,11 +19,8 @@ fn repeats_anywhere(expr: &Expr, config: &VulnerabilityConfig) -> bool { Expr::Repeat { .. } => true, // no nested expressions - Expr::Empty => false, Expr::Token => false, Expr::Assertion(_) => false, - Expr::ContinueFromPreviousMatchEnd => false, - Expr::BackrefExistsCondition(_) => false, // propagate Expr::Concat(list) => list.iter().any(|e| repeats_anywhere(e, config)), @@ -35,9 +34,12 @@ fn repeats_anywhere(expr: &Expr, config: &VulnerabilityConfig) -> bool { true_branch, false_branch, } => { - repeats_anywhere(condition.as_ref(), config) - || repeats_anywhere(true_branch.as_ref(), config) - || repeats_anywhere(false_branch.as_ref(), config) + match condition { + ExprConditional::BackrefExistsCondition(_) => false, + ExprConditional::Condition(condition) => repeats_anywhere(condition.as_ref(), config) + || repeats_anywhere(true_branch.as_ref(), config) + || repeats_anywhere(false_branch.as_ref(), config) + } } } } @@ -60,6 +62,13 @@ pub fn vulnerabilities(regex: &str, config: &VulnerabilityConfig) -> Result Result