diff --git a/src/client.rs b/src/client.rs index a7fa16a2f..8f4b02176 100644 --- a/src/client.rs +++ b/src/client.rs @@ -126,7 +126,7 @@ impl NodeFilter { node.tag == self.tag.as_str() && self.attrs.iter().all(|(k, v)| { node.get_attr(k.as_str()) - .is_some_and(|attr| attr.as_str() == v.as_str()) + .is_some_and(|attr| attr == v.as_str()) }) } } diff --git a/src/handlers/notification/device.rs b/src/handlers/notification/device.rs index d8096b9ed..edb781112 100644 --- a/src/handlers/notification/device.rs +++ b/src/handlers/notification/device.rs @@ -14,7 +14,7 @@ pub(crate) async fn handle_encrypt_notification(client: &Arc, nr: &NodeR handle_identity_change(client, nr).await; } else if nr .get_attr("from") - .is_some_and(|v| v.as_str() == wacore_binary::SERVER_JID) + .is_some_and(|v| v == wacore_binary::SERVER_JID) { let first_child_tag = nr .children() diff --git a/src/pair.rs b/src/pair.rs index e830af64f..63714827f 100644 --- a/src/pair.rs +++ b/src/pair.rs @@ -58,10 +58,7 @@ impl Client { )] pub async fn handle_iq(client: &Arc, node: &NodeRef<'_>) -> bool { // Server JID is "s.whatsapp.net" (no @ prefix for server-only JIDs) - if node - .get_attr("from") - .is_none_or(|v| v.as_str() != SERVER_JID) - { + if node.get_attr("from").is_none_or(|v| v != SERVER_JID) { return false; } diff --git a/src/passkey/flow.rs b/src/passkey/flow.rs index 0996cc55b..7b1c755c0 100644 --- a/src/passkey/flow.rs +++ b/src/passkey/flow.rs @@ -482,11 +482,7 @@ impl Client { /// auto-drive it if an authenticator is registered). pub(crate) async fn handle_passkey_notification(client: &Arc, node: Arc) { // The staged rotation is security-sensitive: only honor a server request. - if node - .get() - .get_attr("from") - .is_none_or(|v| v.as_str() != SERVER_JID) - { + if node.get().get_attr("from").is_none_or(|v| v != SERVER_JID) { warn!("ignoring passkey notification from a non-server JID"); return; } @@ -575,11 +571,7 @@ async fn auto_drive_response( /// Handle a `crsc_continuation` notification. Spawned: it awaits an IQ round-trip /// and must not block the receive loop. pub(crate) async fn handle_passkey_continuation(client: &Arc, node: Arc) { - if node - .get() - .get_attr("from") - .is_none_or(|v| v.as_str() != SERVER_JID) - { + if node.get().get_attr("from").is_none_or(|v| v != SERVER_JID) { warn!("ignoring passkey continuation from a non-server JID"); return; } diff --git a/wacore/binary/src/node.rs b/wacore/binary/src/node.rs index 5ef1f0498..0c85fb1e6 100644 --- a/wacore/binary/src/node.rs +++ b/wacore/binary/src/node.rs @@ -1008,6 +1008,77 @@ impl OwnedNodeRef { } } +#[cfg(test)] +mod value_ref_compare_tests { + use super::*; + use crate::jid::Server; + use std::str::FromStr; + + /// Callers compare a `ValueRef` against a literal to decide whether a + /// stanza came from the server, so `v == needle` has to answer exactly what + /// `v.as_str() == needle` answered — including for the server-only shape + /// (`s.whatsapp.net`, no user), which is the one those checks actually use. + #[test] + fn comparing_a_jid_value_matches_comparing_its_rendered_form() { + let jids = [ + "s.whatsapp.net", + "5511999998888@s.whatsapp.net", + "5511999998888:7@s.whatsapp.net", + "5511999998888.2@s.whatsapp.net", + "120363012345678901@g.us", + "123456789012345@lid", + "123456789012345:9@lid", + "123456789.4:17@interop", + "status@broadcast", + "12345.6@hosted.lid", + ]; + let needles = [ + "s.whatsapp.net", + "5511999998888@s.whatsapp.net", + "5511999998888:7@s.whatsapp.net", + "123456789012345@lid", + "", + "not-a-jid", + ]; + + for raw in jids { + let owned = Jid::from_str(raw).unwrap_or_else(|e| panic!("{raw}: {e}")); + let borrowed = ValueRef::Jid(JidRef { + user: NodeStr::Borrowed(&owned.user), + server: owned.server, + agent: owned.agent, + device: owned.device, + integrator: owned.integrator, + }); + let as_string = ValueRef::String(NodeStr::Borrowed(raw)); + + for needle in needles { + assert_eq!( + borrowed.as_str() == needle, + borrowed == needle, + "jid value {raw:?} vs {needle:?}" + ); + assert_eq!( + as_string.as_str() == needle, + as_string == needle, + "string value {raw:?} vs {needle:?}" + ); + } + } + + // The server-only shape is the one the server checks compare against. + let server_only = ValueRef::Jid(JidRef { + user: NodeStr::Borrowed(""), + server: Server::Pn, + agent: 0, + device: 0, + integrator: 0, + }); + assert!(server_only == "s.whatsapp.net"); + assert!(server_only != "5511999998888@s.whatsapp.net"); + } +} + #[cfg(test)] mod owned_node_ref_tests { use super::*;