diff --git a/server/src/chat/timer.rs b/server/src/chat/timer.rs index 4525b33f..61b43ae3 100644 --- a/server/src/chat/timer.rs +++ b/server/src/chat/timer.rs @@ -483,10 +483,8 @@ fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a } fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - if text.len() < prefix.len() { - return None; - } - if !text[..prefix.len()].eq_ignore_ascii_case(prefix) { + let candidate = text.get(..prefix.len())?; + if !candidate.eq_ignore_ascii_case(prefix) { return None; } let rest = &text[prefix.len()..]; diff --git a/server/src/chat/transport/matrix/assign.rs b/server/src/chat/transport/matrix/assign.rs index 2313e790..ce598b32 100644 --- a/server/src/chat/transport/matrix/assign.rs +++ b/server/src/chat/transport/matrix/assign.rs @@ -254,10 +254,8 @@ fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a } fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - if text.len() < prefix.len() { - return None; - } - if !text[..prefix.len()].eq_ignore_ascii_case(prefix) { + let candidate = text.get(..prefix.len())?; + if !candidate.eq_ignore_ascii_case(prefix) { return None; } let rest = &text[prefix.len()..]; @@ -354,6 +352,18 @@ mod tests { assert_eq!(cmd, None); } + #[test] + fn extract_assign_command_multibyte_prefix_no_panic() { + // "xxxx⏺ assign 42 opus" — ⏺ (U+23FA) is 3 bytes, starting at byte 4. + // "@timmy" has len 6 so text[..6] lands inside ⏺ — panics without the fix. + let cmd = extract_assign_command( + "xxxx\u{23FA} assign 42 opus", + "Timmy", + "@timmy:home.local", + ); + assert_eq!(cmd, None); + } + // -- resolve_agent_name -------------------------------------------------- #[test] diff --git a/server/src/chat/transport/matrix/delete.rs b/server/src/chat/transport/matrix/delete.rs index 4b990156..7e1a94ef 100644 --- a/server/src/chat/transport/matrix/delete.rs +++ b/server/src/chat/transport/matrix/delete.rs @@ -206,10 +206,8 @@ fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a } fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - if text.len() < prefix.len() { - return None; - } - if !text[..prefix.len()].eq_ignore_ascii_case(prefix) { + let candidate = text.get(..prefix.len())?; + if !candidate.eq_ignore_ascii_case(prefix) { return None; } let rest = &text[prefix.len()..]; diff --git a/server/src/chat/transport/matrix/htop.rs b/server/src/chat/transport/matrix/htop.rs index 243c971c..98226df9 100644 --- a/server/src/chat/transport/matrix/htop.rs +++ b/server/src/chat/transport/matrix/htop.rs @@ -110,10 +110,8 @@ fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a } fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - if text.len() < prefix.len() { - return None; - } - if !text[..prefix.len()].eq_ignore_ascii_case(prefix) { + let candidate = text.get(..prefix.len())?; + if !candidate.eq_ignore_ascii_case(prefix) { return None; } let rest = &text[prefix.len()..]; diff --git a/server/src/chat/transport/matrix/rebuild.rs b/server/src/chat/transport/matrix/rebuild.rs index 9da471d3..bec3627a 100644 --- a/server/src/chat/transport/matrix/rebuild.rs +++ b/server/src/chat/transport/matrix/rebuild.rs @@ -74,10 +74,8 @@ fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a } fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - if text.len() < prefix.len() { - return None; - } - if !text[..prefix.len()].eq_ignore_ascii_case(prefix) { + let candidate = text.get(..prefix.len())?; + if !candidate.eq_ignore_ascii_case(prefix) { return None; } let rest = &text[prefix.len()..]; diff --git a/server/src/chat/transport/matrix/reset.rs b/server/src/chat/transport/matrix/reset.rs index afe5c936..cca78812 100644 --- a/server/src/chat/transport/matrix/reset.rs +++ b/server/src/chat/transport/matrix/reset.rs @@ -76,10 +76,8 @@ fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a } fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - if text.len() < prefix.len() { - return None; - } - if !text[..prefix.len()].eq_ignore_ascii_case(prefix) { + let candidate = text.get(..prefix.len())?; + if !candidate.eq_ignore_ascii_case(prefix) { return None; } let rest = &text[prefix.len()..]; diff --git a/server/src/chat/transport/matrix/rmtree.rs b/server/src/chat/transport/matrix/rmtree.rs index 8baf632e..867dbff5 100644 --- a/server/src/chat/transport/matrix/rmtree.rs +++ b/server/src/chat/transport/matrix/rmtree.rs @@ -136,10 +136,8 @@ fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a } fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - if text.len() < prefix.len() { - return None; - } - if !text[..prefix.len()].eq_ignore_ascii_case(prefix) { + let candidate = text.get(..prefix.len())?; + if !candidate.eq_ignore_ascii_case(prefix) { return None; } let rest = &text[prefix.len()..]; diff --git a/server/src/chat/transport/matrix/start.rs b/server/src/chat/transport/matrix/start.rs index 683b176a..b2362bdc 100644 --- a/server/src/chat/transport/matrix/start.rs +++ b/server/src/chat/transport/matrix/start.rs @@ -197,10 +197,8 @@ fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a } fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - if text.len() < prefix.len() { - return None; - } - if !text[..prefix.len()].eq_ignore_ascii_case(prefix) { + let candidate = text.get(..prefix.len())?; + if !candidate.eq_ignore_ascii_case(prefix) { return None; } let rest = &text[prefix.len()..];