From 45b68909b10a615cc6bc09112262c2424c2cb355 Mon Sep 17 00:00:00 2001 From: Noah Whited Date: Wed, 21 May 2025 14:09:08 -0600 Subject: [PATCH 1/2] Added simple bracket matching. I decided to go with the existing loop style as the `wildcard_match` function for continuity, simplicity, and index control. --- src/sudo/env/wildcard_match.rs | 76 +++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/sudo/env/wildcard_match.rs b/src/sudo/env/wildcard_match.rs index eef314605..397d3d9db 100644 --- a/src/sudo/env/wildcard_match.rs +++ b/src/sudo/env/wildcard_match.rs @@ -1,4 +1,4 @@ -/// Match a test input with a pattern +/// Match a test input with a pattern /// Only wildcard characters (*) in the pattern string have a special meaning: they match on zero or more characters pub(super) fn wildcard_match(test: &[u8], pattern: &[u8]) -> bool { let mut test_index = 0; @@ -44,9 +44,48 @@ pub(super) fn wildcard_match(test: &[u8], pattern: &[u8]) -> bool { } } +#[allow(dead_code)] +pub(super) fn bracket_match(test: &[u8], pattern: &[u8]) -> bool { + // Can we assume a correct pattern from the start? + + let mut match_cases: Vec = Vec::new(); + let mut pattern_index = 0; + let mut is_negated = false; + let mut last_dash = None; + + while let Some(p) = pattern.get(pattern_index) { + if *p == b'[' || *p == b']' { + pattern_index += 1; + } else if *p == b'!' || *p == b'^' { + pattern_index += 1; + is_negated = true; + } else if *p == b'-' { + pattern_index += 1; + last_dash = Some(pattern_index); + } else if last_dash.is_some() { + let last_push = match_cases.last().unwrap(); + for case in *last_push..=*p { + match_cases.push(case); + } + last_dash = None; + pattern_index += 1; + } else { + match_cases.push(*p); + pattern_index += 1; + } + } + + if is_negated { + !test.iter().any(|c| match_cases.contains(c)) + } else { + test.iter().any(|c| match_cases.contains(c)) + } +} + #[cfg(test)] mod tests { use super::wildcard_match; + use super::bracket_match; #[test] fn test_wildcard_match() { @@ -83,4 +122,39 @@ mod tests { ); } } + + #[test] + fn test_bracket_match() { + let tests = vec![ + ("foo", "[aeiou]", true), + ("foo", "[xyz]", false), + ("123", "[321]", true), + ("123", "[456]", false), + ("foo", "[xyz][fgh]", true), + ("foo", "[AEIOU]", false), + ("FOO", "[AEIOU]", true), + ("foo", "[a-z]", true), + ("foo", "[A-Z]", false), + ("FOO", "[A-Z]", true), + ("123", "[0-9]", true), + ("foo", "[0-9]", false), + ("foo", "[abc][123][e-j]", true), + ("foo", "[^abc]", true), + ("foo", "[!fgh]", false), + ("foo", "[!a-c][!x-z]", true), + ("123", "[^5-9]", true), + ("foo bar", "[A-Za-z0-9]", true), + ]; + + for (test, pattern, expected) in tests.into_iter() { + assert_eq!( + bracket_match(test.as_bytes(), pattern.as_bytes()), + expected, + "\"{}\" {} match {}", + test, + if expected { "should" } else { "should not" }, + pattern + ); + } + } } From 3a940ab36041ba15555fc492fbe4be750ba7166c Mon Sep 17 00:00:00 2001 From: Noah Whited Date: Wed, 21 May 2025 22:54:57 -0600 Subject: [PATCH 2/2] formatting --- src/sudo/env/wildcard_match.rs | 66 +++++++++++++++++----------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/sudo/env/wildcard_match.rs b/src/sudo/env/wildcard_match.rs index 397d3d9db..5d32ea73d 100644 --- a/src/sudo/env/wildcard_match.rs +++ b/src/sudo/env/wildcard_match.rs @@ -46,8 +46,6 @@ pub(super) fn wildcard_match(test: &[u8], pattern: &[u8]) -> bool { #[allow(dead_code)] pub(super) fn bracket_match(test: &[u8], pattern: &[u8]) -> bool { - // Can we assume a correct pattern from the start? - let mut match_cases: Vec = Vec::new(); let mut pattern_index = 0; let mut is_negated = false; @@ -84,8 +82,8 @@ pub(super) fn bracket_match(test: &[u8], pattern: &[u8]) -> bool { #[cfg(test)] mod tests { - use super::wildcard_match; use super::bracket_match; + use super::wildcard_match; #[test] fn test_wildcard_match() { @@ -124,37 +122,37 @@ mod tests { } #[test] - fn test_bracket_match() { - let tests = vec![ - ("foo", "[aeiou]", true), - ("foo", "[xyz]", false), - ("123", "[321]", true), - ("123", "[456]", false), - ("foo", "[xyz][fgh]", true), - ("foo", "[AEIOU]", false), - ("FOO", "[AEIOU]", true), - ("foo", "[a-z]", true), - ("foo", "[A-Z]", false), - ("FOO", "[A-Z]", true), - ("123", "[0-9]", true), - ("foo", "[0-9]", false), - ("foo", "[abc][123][e-j]", true), - ("foo", "[^abc]", true), - ("foo", "[!fgh]", false), - ("foo", "[!a-c][!x-z]", true), - ("123", "[^5-9]", true), - ("foo bar", "[A-Za-z0-9]", true), - ]; + fn test_bracket_match() { + let tests = vec![ + ("foo", "[aeiou]", true), + ("foo", "[xyz]", false), + ("123", "[321]", true), + ("123", "[456]", false), + ("foo", "[xyz][fgh]", true), + ("foo", "[AEIOU]", false), + ("FOO", "[AEIOU]", true), + ("foo", "[a-z]", true), + ("foo", "[A-Z]", false), + ("FOO", "[A-Z]", true), + ("123", "[0-9]", true), + ("foo", "[0-9]", false), + ("foo", "[abc][123][e-j]", true), + ("foo", "[^abc]", true), + ("foo", "[!fgh]", false), + ("foo", "[!a-c][!x-z]", true), + ("123", "[^5-9]", true), + ("foo bar", "[A-Za-z0-9]", true), + ]; - for (test, pattern, expected) in tests.into_iter() { - assert_eq!( - bracket_match(test.as_bytes(), pattern.as_bytes()), - expected, - "\"{}\" {} match {}", - test, - if expected { "should" } else { "should not" }, - pattern - ); - } + for (test, pattern, expected) in tests.into_iter() { + assert_eq!( + bracket_match(test.as_bytes(), pattern.as_bytes()), + expected, + "\"{}\" {} match {}", + test, + if expected { "should" } else { "should not" }, + pattern + ); } + } }