From 2d8a68e4d15a356aa1b108abc4eb47219284978f Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 5 Jun 2026 01:49:31 +0200 Subject: [PATCH] fix: extract matrix password from backtick code span (real conduit format) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The live conduit admin-room reply (observed directly in #admins) is: Successfully reset the password for user @x:server: `` The delimiter is ': ' after the user id, NOT ' to:' — so the ' to: ' marker added earlier never matched the real format, and extract_new_password returned None for every reply, timing out auto-recovery for every agent. conduit always renders the password as a backtick code span, so anchor on that directly: take the content of the first backtick pair when the message is a password-reset success. Guard against a code-spanned matrix user id in an error message (a real password has no whitespace and isn't @localpart:server). Prose markers stay as a fallback for hypothetical non-code-span builds. Tests cover the exact live format and the code-spanned-user-id error case. --- hive-c0re/src/matrix.rs | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/hive-c0re/src/matrix.rs b/hive-c0re/src/matrix.rs index 4fd5e1c9..7e1247fa 100644 --- a/hive-c0re/src/matrix.rs +++ b/hive-c0re/src/matrix.rs @@ -365,6 +365,32 @@ fn extract_new_password(bot_message: &str) -> Option { // ASCII lowercase: same byte length as the original, so positions from // `lower.find(marker)` are valid byte indices into `bot_message`. let lower = bot_message.to_ascii_lowercase(); + + // Primary strategy: the conduit/tuwunel admin bot always renders the new + // password as a backtick code span. The live reply observed in the admin + // room is: + // "Successfully reset the password for user @x:server: ``" + // (note the delimiter is ": " after the user id, NOT " to:" — the prose + // wording varies between builds, so anchoring on the code span is the + // robust extraction). Take the content of the first backtick pair when the + // message is a password-reset success. Guard against grabbing a code-spanned + // matrix user id ("@x:server") from an error message: a real password has + // no whitespace and isn't a `@localpart:server` id. + if lower.contains("password") + && let Some(open) = bot_message.find('`') + { + let after = &bot_message[open + 1..]; + if let Some(close) = after.find('`') { + let pw = &after[..close]; + if !pw.is_empty() + && !pw.contains(char::is_whitespace) + && !(pw.starts_with('@') && pw.contains(':')) + { + return Some(pw.to_owned()); + } + } + } + for marker in &[ // Explicit "is:" variants (most common in conduwuit / tuwunel): "new password is: ", @@ -503,6 +529,24 @@ mod extract_new_password_tests { assert_eq!(extract_new_password(msg).as_deref(), Some("hunter2")); } + #[test] + fn conduit_live_admin_room_format() { + // The ACTUAL reply observed in the live #admins room — the delimiter + // is ": " after the user id (no " to:"), password in a code span. + let msg = "Successfully reset the password for user @triage:pr1ma.darkest.space: `hVfa6TpvIKnADoEJNWn9saHoI`"; + assert_eq!( + extract_new_password(msg).as_deref(), + Some("hVfa6TpvIKnADoEJNWn9saHoI") + ); + } + + #[test] + fn codespan_userid_in_error_not_mistaken_for_password() { + // An error that code-spans the user id must not yield it as a password. + let msg = "Failed to reset password for `@sock:pr1ma.darkest.space` — user not found"; + assert_eq!(extract_new_password(msg), None); + } + #[test] fn bare_new_password_colon() { let msg = "New password: P@ssword1";