From 80c66dd29c0ed0d4188b1e144a631432bb9b1a57 Mon Sep 17 00:00:00 2001 From: Tristan F <26509014+LeoDog896@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:52:20 -0500 Subject: [PATCH] feat: base safe test suite --- crates/redos/src/ilq.rs | 5 ++-- crates/redos/src/ir.rs | 2 +- crates/redos/src/lib.rs | 2 +- crates/redos/src/vulnerability.rs | 13 ++++++++-- crates/redos/tests/lib.rs | 41 ++++++++++++++++++++++++++----- crates/redos/tests/safe.txt | 30 ++++++++++++++++++++++ fixtures/redos.toml | 1 - 7 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 crates/redos/tests/safe.txt delete mode 100644 fixtures/redos.toml diff --git a/crates/redos/src/ilq.rs b/crates/redos/src/ilq.rs index 041ed96..18d9b86 100644 --- a/crates/redos/src/ilq.rs +++ b/crates/redos/src/ilq.rs @@ -46,10 +46,11 @@ pub fn scan_ilq(expr: &Expr) -> IlqReturn { // luckily, we can just pretend as if the child is the root of its own tree Expr::Repeat(e) => scan_ilq(e), + // TODO: proper support for lookarounds Expr::LookAround(e, _) => scan_ilq(e), - // TODO: atomic groups and lookarounds - _ => IlqReturn::new(true), + // TODO: proper support for atomic groups + Expr::AtomicGroup(e) => scan_ilq(e), } } diff --git a/crates/redos/src/ir.rs b/crates/redos/src/ir.rs index ebc538e..53779a8 100644 --- a/crates/redos/src/ir.rs +++ b/crates/redos/src/ir.rs @@ -123,7 +123,7 @@ pub fn to_expr( let range = hi - lo; let expression = to_expr(child, config, group_increment); - let expression = if range > config.max_quantifier { + let expression = if range > config.four_max_quantifier { expression.map(|child| Expr::Repeat(Box::new(child))) } else { expression diff --git a/crates/redos/src/lib.rs b/crates/redos/src/lib.rs index 82f5cc1..2e425d3 100644 --- a/crates/redos/src/lib.rs +++ b/crates/redos/src/lib.rs @@ -34,7 +34,7 @@ impl RegexInfo { /// /// A regex must meet the following criteria to be even considered to be vulnerable: /// - It must contain a repeat -/// - The repeat must have a bound size greater than `config.max_quantifier` +/// - The repeat must have a bound size greater than `config.second_max_quantifier` /// - The regex must have a terminating state (to allow for backtracking) (TODO: this is not implemented yet) fn regex_pre_scan(expr: &Expr) -> RegexInfo { match expr { diff --git a/crates/redos/src/vulnerability.rs b/crates/redos/src/vulnerability.rs index 7900f52..6b504e2 100644 --- a/crates/redos/src/vulnerability.rs +++ b/crates/redos/src/vulnerability.rs @@ -9,7 +9,9 @@ pub enum Vulnerability { impl Default for VulnerabilityConfig { fn default() -> Self { Self { - max_quantifier: 100, + two_max_quantifier: 1000, + three_max_quantifier: 100, + four_max_quantifier: 10, } } } @@ -23,5 +25,12 @@ pub enum Complexity { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct VulnerabilityConfig { /// The max size an upper bound can be before it is considered "large" - pub max_quantifier: usize, + /// in a superlinear fashion + pub two_max_quantifier: usize, + /// The max size an upper bound can be before it is considered "large" + /// for 3rd degree polynomials + pub three_max_quantifier: usize, + /// The max size an upper bound can be before it is considered "large" + /// for 4th degree polynomials + pub four_max_quantifier: usize, } diff --git a/crates/redos/tests/lib.rs b/crates/redos/tests/lib.rs index ebd8a62..88702e0 100644 --- a/crates/redos/tests/lib.rs +++ b/crates/redos/tests/lib.rs @@ -2,19 +2,48 @@ mod tests { use redos::vulnerabilities; - fn assert_safe(regex: &str) { + static SAFE: &str = include_str!("safe.txt"); + + /// Takes a test file and returns pairs + /// of test names and their contents + fn parse_test_file(file: &str) -> Vec<(String, Vec)> { + let mut tests: Vec<(String, Vec)> = vec![]; + + for line in file.lines() { + if line.starts_with('#') { + continue; + } + + if line.starts_with('\t') { + let mut line = line.to_string(); + line.pop(); + tests.last_mut().unwrap().1.push(line); + } else { + tests.push((line.to_string(), vec![])); + } + } + + tests + } + + fn assert_safe(regex: &str, message: &str) { assert_eq!( vulnerabilities(regex, &Default::default()) .unwrap() .vulnerabilities, - vec![] + vec![], + "{} failed: {}", + message, + regex ); } #[test] - fn trivial_regexes() { - assert_safe("abc"); - assert_safe("(abc|def)|[nhi]?"); - assert_safe("a{1,43}"); + fn check_safe() { + for (name, tests) in parse_test_file(SAFE) { + for test in tests { + assert_safe(&test, &name); + } + } } } diff --git a/crates/redos/tests/safe.txt b/crates/redos/tests/safe.txt new file mode 100644 index 0000000..d139af8 --- /dev/null +++ b/crates/redos/tests/safe.txt @@ -0,0 +1,30 @@ +base example + abc +lets try out groups + (abc|def)|[nhi]? +tiny repeating string + a{1,10} +bigger repeated string that's just right + a{} +nested group + (((a))) +try putting some quantifiers outside + ((a+)+)+ +are optionals detected? + (a?)+ +lets try lookarounds + (?<=a) + (?a) + (?>a+) +can we decompose alternations? + (a+)|(b+)|(((a))) +how about alternations in lookarounds? + (?<=a|b) + (?a|b) + (?>a+|b+) diff --git a/fixtures/redos.toml b/fixtures/redos.toml deleted file mode 100644 index 0637a08..0000000 --- a/fixtures/redos.toml +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file