From 411d9c0cc6a014cd1f720574c56aedd774f9a944 Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Thu, 5 Mar 2026 16:47:58 +0100 Subject: [PATCH 01/12] fix(redis): use Unicode-aware uppercasing in quantize_redis_string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Go's strings.ToUpper() handles Unicode (e.g. Cherokee small letters like ꭺ→Ꭺ), while to_ascii_uppercase() only covers ASCII. Switch to to_uppercase() to match Go's behavior. Fixes fuzzing testcase: redis_quantize_fuzzing_3286489773 --- libdd-trace-obfuscation/src/redis.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index 8aaf1df38e..e0fcc8ce1d 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -29,7 +29,7 @@ pub fn quantize_redis_string(query: &str) -> String { continue; } - let cmd = first.to_ascii_uppercase(); + let cmd = first.to_uppercase(); let command = match cmd.as_bytes() { b"CLIENT" | b"CLUSTER" | b"COMMAND" | b"CONFIG" | b"DEBUG" | b"SCRIPT" => { match tokens.next() { @@ -37,7 +37,7 @@ pub fn quantize_redis_string(query: &str) -> String { truncated = true; continue; } - Some(sub) => format!("{cmd} {}", sub.to_ascii_uppercase()), + Some(sub) => format!("{cmd} {}", sub.to_uppercase()), None => cmd, } } @@ -420,6 +420,11 @@ mod tests { input ["UNKNOWN 123"] expected ["UNKNOWN"]; ] + [ + test_name [fuzzing_3286489773] + input ["ꭺ"] + expected ["Ꭺ"]; + ] )] #[test] fn test_name() { From 8169bc57c1d13b4075f7a72ad10a1fae932e1ae5 Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Thu, 5 Mar 2026 17:07:57 +0100 Subject: [PATCH 02/12] fix(redis): trim only spaces in quantize_redis_string, not all whitespace Go's QuantizeRedisString uses strings.Trim(rawLine, " ") which only strips ASCII spaces. Rust's .trim() strips all whitespace including tabs. A tab-only input should be kept as a command ("\t"), not silently dropped. Fixes fuzzing testcase: redis_quantize_fuzzing_2812552373 --- libdd-trace-obfuscation/src/redis.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index e0fcc8ce1d..19a7c4227d 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -16,7 +16,9 @@ pub fn quantize_redis_string(query: &str) -> String { break; } - let line = raw_line.trim(); + // Go's QuantizeRedisString trims only ASCII spaces (strings.Trim(rawLine, " ")), + // not all whitespace. Use trim_matches(' ') to match that behavior. + let line = raw_line.trim_matches(' '); if line.is_empty() { continue; } @@ -425,6 +427,11 @@ mod tests { input ["ꭺ"] expected ["Ꭺ"]; ] + [ + test_name [fuzzing_2812552373] + input ["\t"] + expected ["\t"]; + ] )] #[test] fn test_name() { From af73a122f8bc206e19720c29f285127eaed4a4ae Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Thu, 5 Mar 2026 17:11:45 +0100 Subject: [PATCH 03/12] fix(redis): split on spaces only (not all whitespace) in quantize_redis_string Go's QuantizeRedisString uses strings.SplitN(line, " ", 3) to split tokens, which only splits on ASCII spaces. Rust's split_whitespace() splits on all whitespace including tabs, causing "\t" to yield no tokens and be dropped. Switch to split(' ').filter(|s| !s.is_empty()) to match Go's behavior. Fixes fuzzing testcase: redis_quantize_fuzzing_2812552373 --- libdd-trace-obfuscation/src/redis.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index 19a7c4227d..46d347f86f 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -23,7 +23,9 @@ pub fn quantize_redis_string(query: &str) -> String { continue; } - let mut tokens = line.split_whitespace(); + // Go splits on spaces only (strings.SplitN(line, " ", 3)), not all whitespace. + // Use split(' ').filter to match that behavior and preserve tab tokens. + let mut tokens = line.split(' ').filter(|s| !s.is_empty()); let Some(first) = tokens.next() else { continue }; if first.ends_with(REDIS_TRUNCATION_MARK) { From 7b2c40fa3a9f680cdf1e4988a545c189e963f10f Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Thu, 5 Mar 2026 19:01:07 +0100 Subject: [PATCH 04/12] fix(redis): preserve tabs before command tokens (Go only skips spaces, not tabs) --- libdd-trace-obfuscation/src/redis_tokenizer.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libdd-trace-obfuscation/src/redis_tokenizer.rs b/libdd-trace-obfuscation/src/redis_tokenizer.rs index 0707b63aa4..2142c786b1 100644 --- a/libdd-trace-obfuscation/src/redis_tokenizer.rs +++ b/libdd-trace-obfuscation/src/redis_tokenizer.rs @@ -47,7 +47,8 @@ impl<'a> RedisTokenizer<'a> { RedisTokenType::RedisTokenArgument => self.next_arg(), }; loop { - self.skip_whitespace(); + // Only skip spaces between commands (not tabs - Go only skips spaces) + while self.curr_char() == b' ' { self.offset += 1; } if self.curr_char() != b'\n' { break; } @@ -58,7 +59,11 @@ impl<'a> RedisTokenizer<'a> { } fn next_cmd(&mut self) -> (usize, usize) { - self.skip_whitespace(); + // Go's scanCommand only skips ASCII spaces before the command (not tabs). + // Tabs are included in the command token (default case in Go's switch). + while self.curr_char() == b' ' { + self.offset += 1; + } let start = self.offset; loop { match self.curr_char() { From 1203bf4b2b8c217ab3211340c9a1d676d54139a9 Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Thu, 5 Mar 2026 19:07:14 +0100 Subject: [PATCH 05/12] fix(redis): preserve tabs in command tokens, skip whitespace-only trailing commands --- libdd-trace-obfuscation/src/redis.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index 46d347f86f..068eeea4bf 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -82,7 +82,15 @@ pub fn obfuscate_redis_string(cmd: &str) -> String { RedisTokenType::RedisTokenArgument => args.push(res.token), } if res.done { - obfuscate_redis_cmd(s, cmd.unwrap_or_default(), args); + // Skip whitespace-only final "command" tokens (trailing whitespace after last \n). + // Also strip the trailing '\n' that was added before this whitespace-only token. + let final_cmd = cmd.unwrap_or_default(); + if !final_cmd.trim().is_empty() { + obfuscate_redis_cmd(s, final_cmd, args); + } else { + // Remove the trailing '\n' added when this whitespace-only command was scanned + if s.ends_with('\n') { s.pop(); } + } break; } } From cea11a5066dbde473b0af5a4a6e53ea3250593d2 Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Thu, 5 Mar 2026 19:32:56 +0100 Subject: [PATCH 06/12] fix(redis): trim input before tokenizing (Go does bytes.TrimSpace) --- libdd-trace-obfuscation/src/redis.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index 068eeea4bf..d907bdbea1 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -63,6 +63,8 @@ pub fn quantize_redis_string(query: &str) -> String { } pub fn obfuscate_redis_string(cmd: &str) -> String { + // Go's newRedisTokenizer calls bytes.TrimSpace before tokenizing + let cmd = cmd.trim(); let mut tokenizer = RedisTokenizer::new(cmd); let s = &mut String::new(); let mut cmd: Option<&str> = None; From 1548bf7d5294c68959e91858b35863aa0ea2aa51 Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Thu, 5 Mar 2026 19:45:12 +0100 Subject: [PATCH 07/12] fix(redis): split quantize on \n only (preserves \r like Go's strings.IndexByte) --- libdd-trace-obfuscation/src/redis.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index d907bdbea1..5111d9bb84 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -11,7 +11,8 @@ pub fn quantize_redis_string(query: &str) -> String { let mut commands: Vec = Vec::with_capacity(MAX_REDIS_NB_COMMANDS); let mut truncated = false; - for raw_line in query.lines() { + // Split on '\n' only (like Go's strings.IndexByte), preserving '\r' in line content + for raw_line in query.split('\n') { if commands.len() >= MAX_REDIS_NB_COMMANDS { break; } From 477d9cacdfe0a636a1759e03dba7196b3ad0c9ab Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Thu, 5 Mar 2026 20:35:16 +0100 Subject: [PATCH 08/12] fix(redis): use single-rune uppercase (unicode.ToUpper parity) in quantize --- libdd-trace-obfuscation/src/redis.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index 5111d9bb84..553d192ed5 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -34,7 +34,17 @@ pub fn quantize_redis_string(query: &str) -> String { continue; } - let cmd = first.to_uppercase(); + // Go's strings.ToUpper uses unicode.ToUpper which maps each rune to exactly one uppercase + // rune. Rust's str::to_uppercase() uses full Unicode case folding which can expand a single + // char to multiple (e.g. some Greek letters with diacritics). To match Go: uppercase when + // it maps to exactly one char, otherwise keep the original. + let cmd: String = first.chars().map(|c| { + let mut upper = c.to_uppercase(); + match (upper.next(), upper.next()) { + (Some(u), None) => u, // single-char mapping: use it + _ => c, // multi-char expansion: keep original (matches Go) + } + }).collect(); let command = match cmd.as_bytes() { b"CLIENT" | b"CLUSTER" | b"COMMAND" | b"CONFIG" | b"DEBUG" | b"SCRIPT" => { match tokens.next() { @@ -42,7 +52,7 @@ pub fn quantize_redis_string(query: &str) -> String { truncated = true; continue; } - Some(sub) => format!("{cmd} {}", sub.to_uppercase()), + Some(sub) => format!("{cmd} {}", sub.chars().map(|c| { let mut u = c.to_uppercase(); match (u.next(), u.next()) { (Some(up), None) => up, _ => c } }).collect::()), None => cmd, } } From 9c2342b9e957b5b06f439cbbc21b57f06ce98795 Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Fri, 6 Mar 2026 11:46:26 +0100 Subject: [PATCH 09/12] fix(redis): skip Unicode 16.0 case pairs Go 1.25 doesn't know about MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rust uses Unicode 16.0 which added uppercase mappings for U+16E80–U+16EFF (Bamum Supplement) that Go 1.25 (Unicode 15.0) doesn't have. Extracted a go_toupper helper that mirrors Go's unicode.ToUpper: uses to_uppercase() for single-char mappings but leaves the Bamum range unchanged to match Go. --- libdd-trace-obfuscation/src/redis.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index 553d192ed5..16959cafb4 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -6,6 +6,20 @@ use crate::redis_tokenizer::{RedisTokenType, RedisTokenizer}; const REDIS_TRUNCATION_MARK: &str = "..."; const MAX_REDIS_NB_COMMANDS: usize = 3; +/// Uppercase a single char to match Go's unicode.ToUpper (Unicode 15.0). +/// Rust uses Unicode 16.0 which added case pairs for U+16E80–U+16EFF (Bamum Supplement) +/// that Go 1.25 doesn't know about — keep those chars unchanged to match Go. +fn go_toupper(c: char) -> char { + if (0x16E80..=0x16EFF).contains(&(c as u32)) { + return c; + } + let mut upper = c.to_uppercase(); + match (upper.next(), upper.next()) { + (Some(u), None) => u, + _ => c, + } +} + /// Returns a quantized version of a Redis query, keeping only up to 3 command names. pub fn quantize_redis_string(query: &str) -> String { let mut commands: Vec = Vec::with_capacity(MAX_REDIS_NB_COMMANDS); @@ -34,17 +48,7 @@ pub fn quantize_redis_string(query: &str) -> String { continue; } - // Go's strings.ToUpper uses unicode.ToUpper which maps each rune to exactly one uppercase - // rune. Rust's str::to_uppercase() uses full Unicode case folding which can expand a single - // char to multiple (e.g. some Greek letters with diacritics). To match Go: uppercase when - // it maps to exactly one char, otherwise keep the original. - let cmd: String = first.chars().map(|c| { - let mut upper = c.to_uppercase(); - match (upper.next(), upper.next()) { - (Some(u), None) => u, // single-char mapping: use it - _ => c, // multi-char expansion: keep original (matches Go) - } - }).collect(); + let cmd: String = first.chars().map(go_toupper).collect(); let command = match cmd.as_bytes() { b"CLIENT" | b"CLUSTER" | b"COMMAND" | b"CONFIG" | b"DEBUG" | b"SCRIPT" => { match tokens.next() { @@ -52,7 +56,7 @@ pub fn quantize_redis_string(query: &str) -> String { truncated = true; continue; } - Some(sub) => format!("{cmd} {}", sub.chars().map(|c| { let mut u = c.to_uppercase(); match (u.next(), u.next()) { (Some(up), None) => up, _ => c } }).collect::()), + Some(sub) => format!("{cmd} {}", sub.chars().map(go_toupper).collect::()), None => cmd, } } From fd9d93891c67faa779ac5bb29326e14a8998f570 Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Fri, 6 Mar 2026 17:04:03 +0100 Subject: [PATCH 10/12] fix(redis): clippy warnings --- libdd-trace-obfuscation/src/redis.rs | 15 ++++++-------- .../src/redis_tokenizer.rs | 20 ++++++++----------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index 16959cafb4..0f1c04d6ed 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -123,29 +123,27 @@ fn obfuscate_redis_cmd<'a>(str: &mut String, cmd: &'a str, mut args: Vec<&'a str let mut uppercase_cmd = [0; 32]; // no redis cmd is longer than 32 chars let uppercase_cmd = ascii_uppercase(cmd, &mut uppercase_cmd).unwrap_or(&[]); match uppercase_cmd { - b"AUTH" | b"MIGRATE" | b"HELLO" => { + b"AUTH" | b"MIGRATE" | b"HELLO" // Obfuscate everything after command: // • AUTH password // • MIGRATE host port key|"" destination-db timeout [COPY] [REPLACE] [AUTH password] // [AUTH2 username password] [KEYS key [key ...]] // • HELLO [protover [AUTH username password] [SETNAME clientname]] - if !args.is_empty() { + if !args.is_empty() => { args.clear(); args.push("?"); } - } - b"ACL" => { + b"ACL" // Obfuscate all arguments after the subcommand: // • ACL SETUSER username on >password ~keys &channels +commands // • ACL GETUSER username // • ACL DELUSER username [username ...] // • ACL LIST // • ACL WHOAMI - if args.len() > 1 { + if args.len() > 1 => { args[1] = "?"; args.drain(2..); } - } b"APPEND" | b"GETSET" | b"LPUSHX" | b"GEORADIUSBYMEMBER" | b"RPUSHX" | b"SET" | b"SETNX" | b"SISMEMBER" | b"ZRANK" | b"ZREVRANK" | b"ZSCORE" => { // Obfuscate 2nd argument: @@ -184,7 +182,7 @@ fn obfuscate_redis_cmd<'a>(str: &mut String, cmd: &'a str, mut args: Vec<&'a str // • LINSERT key BEFORE|AFTER pivot value args = obfuscate_redis_args_n(args, 3); } - b"GEOHASH" | b"GEOPOS" | b"GEODIST" | b"LPUSH" | b"RPUSH" | b"SREM" | b"ZREM" | b"SADD" => { + b"GEOHASH" | b"GEOPOS" | b"GEODIST" | b"LPUSH" | b"RPUSH" | b"SREM" | b"ZREM" | b"SADD" // Obfuscate all arguments after the first one. // • GEOHASH key member [member ...] // • GEOPOS key member [member ...] @@ -194,11 +192,10 @@ fn obfuscate_redis_cmd<'a>(str: &mut String, cmd: &'a str, mut args: Vec<&'a str // • SREM key member [member ...] // • ZREM key member [member ...] // • SADD key member [member ...] - if args.len() > 1 { + if args.len() > 1 => { args[1] = "?"; args.drain(2..); } - } b"GEOADD" => { // Obfuscating every 3rd argument starting from first // • GEOADD key longitude latitude member [longitude latitude member ...] diff --git a/libdd-trace-obfuscation/src/redis_tokenizer.rs b/libdd-trace-obfuscation/src/redis_tokenizer.rs index 2142c786b1..897af6341a 100644 --- a/libdd-trace-obfuscation/src/redis_tokenizer.rs +++ b/libdd-trace-obfuscation/src/redis_tokenizer.rs @@ -91,31 +91,27 @@ impl<'a> RedisTokenizer<'a> { loop { match self.curr_char() { 0 => break, - b'\\' => { - if !escape { + b'\\' + if !escape => { escape = true; self.offset += 1; continue; } - } - b'"' => { - if !escape { + b'"' + if !escape => { quote = !quote } - } - b'\n' => { - if !quote { + b'\n' + if !quote => { let span = (start, self.offset); self.offset += 1; self.state = RedisTokenType::RedisTokenCommand; return span; } - } - b' ' => { - if !quote { + b' ' + if !quote => { return (start, self.offset); } - } _ => {} } escape = false; From 55c0e585ee07018489306cb0f54874421366edcf Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Mon, 9 Mar 2026 16:05:32 +0100 Subject: [PATCH 11/12] fix(redis): cargo fmt --- libdd-trace-obfuscation/src/redis.rs | 8 +++- .../src/redis_tokenizer.rs | 40 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index 0f1c04d6ed..c01098dbb2 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -56,7 +56,9 @@ pub fn quantize_redis_string(query: &str) -> String { truncated = true; continue; } - Some(sub) => format!("{cmd} {}", sub.chars().map(go_toupper).collect::()), + Some(sub) => { + format!("{cmd} {}", sub.chars().map(go_toupper).collect::()) + } None => cmd, } } @@ -106,7 +108,9 @@ pub fn obfuscate_redis_string(cmd: &str) -> String { obfuscate_redis_cmd(s, final_cmd, args); } else { // Remove the trailing '\n' added when this whitespace-only command was scanned - if s.ends_with('\n') { s.pop(); } + if s.ends_with('\n') { + s.pop(); + } } break; } diff --git a/libdd-trace-obfuscation/src/redis_tokenizer.rs b/libdd-trace-obfuscation/src/redis_tokenizer.rs index 897af6341a..e5f8982794 100644 --- a/libdd-trace-obfuscation/src/redis_tokenizer.rs +++ b/libdd-trace-obfuscation/src/redis_tokenizer.rs @@ -48,7 +48,9 @@ impl<'a> RedisTokenizer<'a> { }; loop { // Only skip spaces between commands (not tabs - Go only skips spaces) - while self.curr_char() == b' ' { self.offset += 1; } + while self.curr_char() == b' ' { + self.offset += 1; + } if self.curr_char() != b'\n' { break; } @@ -91,27 +93,21 @@ impl<'a> RedisTokenizer<'a> { loop { match self.curr_char() { 0 => break, - b'\\' - if !escape => { - escape = true; - self.offset += 1; - continue; - } - b'"' - if !escape => { - quote = !quote - } - b'\n' - if !quote => { - let span = (start, self.offset); - self.offset += 1; - self.state = RedisTokenType::RedisTokenCommand; - return span; - } - b' ' - if !quote => { - return (start, self.offset); - } + b'\\' if !escape => { + escape = true; + self.offset += 1; + continue; + } + b'"' if !escape => quote = !quote, + b'\n' if !quote => { + let span = (start, self.offset); + self.offset += 1; + self.state = RedisTokenType::RedisTokenCommand; + return span; + } + b' ' if !quote => { + return (start, self.offset); + } _ => {} } escape = false; From e571d6bb0328403875b3ff2b5783849d7d59419b Mon Sep 17 00:00:00 2001 From: Oscar Le Dauphin Date: Mon, 9 Mar 2026 17:01:25 +0100 Subject: [PATCH 12/12] fix(redis): remove useless bit of code, add tests --- libdd-trace-obfuscation/src/redis.rs | 38 ++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/libdd-trace-obfuscation/src/redis.rs b/libdd-trace-obfuscation/src/redis.rs index c01098dbb2..f26f082951 100644 --- a/libdd-trace-obfuscation/src/redis.rs +++ b/libdd-trace-obfuscation/src/redis.rs @@ -101,17 +101,7 @@ pub fn obfuscate_redis_string(cmd: &str) -> String { RedisTokenType::RedisTokenArgument => args.push(res.token), } if res.done { - // Skip whitespace-only final "command" tokens (trailing whitespace after last \n). - // Also strip the trailing '\n' that was added before this whitespace-only token. - let final_cmd = cmd.unwrap_or_default(); - if !final_cmd.trim().is_empty() { - obfuscate_redis_cmd(s, final_cmd, args); - } else { - // Remove the trailing '\n' added when this whitespace-only command was scanned - if s.ends_with('\n') { - s.pop(); - } - } + obfuscate_redis_cmd(s, cmd.unwrap_or_default(), args); break; } } @@ -365,6 +355,11 @@ mod tests { input ["CLIENT LIST"] expected ["CLIENT LIST"]; ] + [ + test_name [test_quantize_redis_string_client_truncated] + input ["CLIENT ..."] + expected ["..."]; + ] [ test_name [test_quantize_redis_string_get_lowercase] input ["get my_key"] @@ -460,6 +455,17 @@ mod tests { input ["\t"] expected ["\t"]; ] + [ + test_name [fuzzing_crlf] + input ["\r\n"] + // Split on \n specifically, not newlines, to copy agent's behaviour + expected ["\r"]; + ] + [ + test_name [fuzzing_box_char] + input ["𖺻"] + expected ["𖺻"]; + ] )] #[test] fn test_name() { @@ -980,6 +986,16 @@ SET k ?"#]; input ["bitfield key SET key value incrby 3"] expected ["bitfield ? SET ? incrby ?"]; ] + [ + test_name [test_obfuscate_fuzzing_unicode] + input ["\u{00b}ჸ"] + expected ["ჸ"]; + ] + [ + test_name [test_obfuscate_fuzzing_whitespaces] + input ["ჸ\n\tჸ"] + expected ["ჸ ?"]; + ] )] #[test] fn test_name() {