diff --git a/src/protocol/messages.rs b/src/protocol/messages.rs index 66c155a..ffa6189 100644 --- a/src/protocol/messages.rs +++ b/src/protocol/messages.rs @@ -426,14 +426,29 @@ impl ResponderAuthBuilder { if self.auth.task.is_some() { panic!("task may not be set"); } - // TODO (#10): Check for duplicate tasks! - match self.auth.tasks { - Some(ref tasks) if tasks.is_empty() => Err(SignalingError::InvalidMessage( - "An `Auth` message must contain at least one task".to_string() - )), - Some(_) => Ok(self.auth), - None => panic!("tasks list not initialized!"), - } + + { // Validate tasks + let tasks = self.auth.tasks.as_ref().expect("tasks list not initialized!"); + + // Ensure that tasks list is not empty + if tasks.is_empty() { + return Err(SignalingError::InvalidMessage( + "An `Auth` message must contain at least one task".to_string() + )); + } + + // Ensure that tasks list does not contain duplicates + let mut cloned = tasks.clone(); + cloned.sort_unstable(); + cloned.dedup(); + if cloned.len() != tasks.len() { + return Err(SignalingError::InvalidMessage( + "An `Auth` message may not contain duplicate tasks".to_string() + )); + } + } // Waiting for NLL + + Ok(self.auth) } } diff --git a/src/protocol/tests/signaling_messages.rs b/src/protocol/tests/signaling_messages.rs index d24df0b..a6df7f8 100644 --- a/src/protocol/tests/signaling_messages.rs +++ b/src/protocol/tests/signaling_messages.rs @@ -1042,6 +1042,40 @@ mod auth { assert_eq!(ctx.signaling.common().signaling_state(), SignalingState::Task); assert_eq!(ctx.signaling.initiator.handshake_state(), InitiatorHandshakeState::AuthReceived); } + + /// Ensure that duplicate names are not allowed when constructing a responder `Auth` message. + #[test] + fn responder_auth_tasks_no_duplicates_simple() { + let simple = ResponderAuthBuilder::new(Cookie::random()) + .add_task("dummy1", None) + .add_task("dummy1", None) + .build(); + assert_eq!( + simple, + Err(SignalingError::InvalidMessage("An `Auth` message may not contain duplicate tasks".into())), + ); + + let nonconsecutive = ResponderAuthBuilder::new(Cookie::random()) + .add_task("dummy1", None) + .add_task("dummy2", None) + .add_task("dummy1", None) + .build(); + assert_eq!( + nonconsecutive, + Err(SignalingError::InvalidMessage("An `Auth` message may not contain duplicate tasks".into())), + ); + + let different_data = ResponderAuthBuilder::new(Cookie::random()) + .add_task("dummy1", None) + .add_task("dummy1", Some({ + let mut data = HashMap::new(); data.insert("a".into(), 1.into()); data + })) + .build(); + assert_eq!( + different_data, + Err(SignalingError::InvalidMessage("An `Auth` message may not contain duplicate tasks".into())), + ); + } } mod new_initiator {