diff --git a/hive-c0re/src/matrix.rs b/hive-c0re/src/matrix.rs index 30a40165..4fd5e1c9 100644 --- a/hive-c0re/src/matrix.rs +++ b/hive-c0re/src/matrix.rs @@ -395,10 +395,15 @@ fn extract_new_password(bot_message: &str) -> Option { ] { if let Some(pos) = lower.find(marker) { let rest = &bot_message[pos + marker.len()..]; - let rest = rest.trim_start(); - // Stop at first whitespace or newline; password must be non-empty. + // Strip leading whitespace, then a leading code-span backtick: + // tuwunel renders the password as a code span, so the plain + // `body` carries literal backticks ("… to: ``"). + let rest = rest.trim_start().trim_start_matches('`'); + // The password ends at the first whitespace OR the closing + // backtick. Generated passwords contain neither, so this never + // truncates a real password mid-token. let end = rest - .find(char::is_whitespace) + .find(|c: char| c.is_whitespace() || c == '`') .unwrap_or(rest.len()); let pw = rest[..end].trim(); if !pw.is_empty() { @@ -484,6 +489,20 @@ mod extract_new_password_tests { assert_eq!(extract_new_password(msg).as_deref(), Some("abc123XYZ")); } + #[test] + fn backtick_wrapped_password() { + // tuwunel renders the password as a code span; the plain body + // carries literal backticks. Strip them, don't capture them. + let msg = "Successfully reset password for @atlas:pr1ma.darkest.space to: `N3wP@ssw0rd`"; + assert_eq!(extract_new_password(msg).as_deref(), Some("N3wP@ssw0rd")); + } + + #[test] + fn backtick_wrapped_with_trailing_text() { + let msg = "Done. New password is: `hunter2` (store it now)"; + assert_eq!(extract_new_password(msg).as_deref(), Some("hunter2")); + } + #[test] fn bare_new_password_colon() { let msg = "New password: P@ssword1";