comparison src/conv.rs @ 93:efc2b56c8928

Remove undefined behavior per MIRI. This replaces a bunch of raw pointers with NonNull and removes all the undefined behavior that we can find with MIRI. We also remove the `SecureString` dependency (since it doesn't work with MIRI, and because it's not really necessary).
author Paul Fisher <paul@pfish.zone>
date Mon, 23 Jun 2025 13:02:58 -0400
parents 05291b601f0a
children db167f96ba46
comparison
equal deleted inserted replaced
92:5ddbcada30f2 93:efc2b56c8928
2 2
3 // Temporarily allowed until we get the actual conversation functions hooked up. 3 // Temporarily allowed until we get the actual conversation functions hooked up.
4 #![allow(dead_code)] 4 #![allow(dead_code)]
5 5
6 use crate::constants::{ErrorCode, Result}; 6 use crate::constants::{ErrorCode, Result};
7 use secure_string::SecureString;
8 use std::cell::Cell; 7 use std::cell::Cell;
9 use std::fmt; 8 use std::fmt;
9 use std::fmt::Debug;
10 use std::result::Result as StdResult; 10 use std::result::Result as StdResult;
11 11
12 /// The types of message and request that can be sent to a user. 12 /// The types of message and request that can be sent to a user.
13 /// 13 ///
14 /// The data within each enum value is the prompt (or other information) 14 /// The data within each enum value is the prompt (or other information)
120 120
121 q_and_a!( 121 q_and_a!(
122 /// A Q&A that asks the user for text and does not show it while typing. 122 /// A Q&A that asks the user for text and does not show it while typing.
123 /// 123 ///
124 /// In other words, a password entry prompt. 124 /// In other words, a password entry prompt.
125 MaskedQAndA<'a, Q=&'a str, A=SecureString>, 125 MaskedQAndA<'a, Q=&'a str, A=String>,
126 Message::MaskedPrompt 126 Message::MaskedPrompt
127 ); 127 );
128 128
129 q_and_a!( 129 q_and_a!(
130 /// A standard Q&A prompt that asks the user for text. 130 /// A standard Q&A prompt that asks the user for text.
282 /// 282 ///
283 /// For example, to use a `Conversation` as a `SimpleConversation`: 283 /// For example, to use a `Conversation` as a `SimpleConversation`:
284 /// 284 ///
285 /// ``` 285 /// ```
286 /// # use nonstick::{Conversation, Result}; 286 /// # use nonstick::{Conversation, Result};
287 /// # use secure_string::SecureString;
288 /// // Bring this trait into scope to get `masked_prompt`, among others. 287 /// // Bring this trait into scope to get `masked_prompt`, among others.
289 /// use nonstick::SimpleConversation; 288 /// use nonstick::SimpleConversation;
290 /// 289 ///
291 /// fn ask_for_token(convo: &mut impl Conversation) -> Result<SecureString> { 290 /// fn ask_for_token(convo: &mut impl Conversation) -> Result<String> {
292 /// convo.masked_prompt("enter your one-time token") 291 /// convo.masked_prompt("enter your one-time token")
293 /// } 292 /// }
294 /// ``` 293 /// ```
295 /// 294 ///
296 /// or to use a `SimpleConversation` as a `Conversation`: 295 /// or to use a `SimpleConversation` as a `Conversation`:
297 /// 296 ///
298 /// ``` 297 /// ```
299 /// use nonstick::{Conversation, SimpleConversation}; 298 /// use nonstick::{Conversation, SimpleConversation};
300 /// use secure_string::SecureString;
301 /// # use nonstick::{BinaryData, Result}; 299 /// # use nonstick::{BinaryData, Result};
302 /// mod some_library { 300 /// mod some_library {
303 /// # use nonstick::Conversation; 301 /// # use nonstick::Conversation;
304 /// pub fn get_auth_data(conv: &mut impl Conversation) { /* ... */ 302 /// pub fn get_auth_data(conv: &mut impl Conversation) { /* ... */
305 /// } 303 /// }
312 /// // ... 310 /// // ...
313 /// # fn prompt(&mut self, request: &str) -> Result<String> { 311 /// # fn prompt(&mut self, request: &str) -> Result<String> {
314 /// # todo!() 312 /// # todo!()
315 /// # } 313 /// # }
316 /// # 314 /// #
317 /// # fn masked_prompt(&mut self, request: &str) -> Result<SecureString> { 315 /// # fn masked_prompt(&mut self, request: &str) -> Result<String> {
318 /// # todo!() 316 /// # todo!()
319 /// # } 317 /// # }
320 /// # 318 /// #
321 /// # fn error_msg(&mut self, message: &str) { 319 /// # fn error_msg(&mut self, message: &str) {
322 /// # todo!() 320 /// # todo!()
353 Demux(self) 351 Demux(self)
354 } 352 }
355 /// Prompts the user for something. 353 /// Prompts the user for something.
356 fn prompt(&mut self, request: &str) -> Result<String>; 354 fn prompt(&mut self, request: &str) -> Result<String>;
357 /// Prompts the user for something, but hides what the user types. 355 /// Prompts the user for something, but hides what the user types.
358 fn masked_prompt(&mut self, request: &str) -> Result<SecureString>; 356 fn masked_prompt(&mut self, request: &str) -> Result<String>;
359 /// Alerts the user to an error. 357 /// Alerts the user to an error.
360 fn error_msg(&mut self, message: &str); 358 fn error_msg(&mut self, message: &str);
361 /// Sends an informational message to the user. 359 /// Sends an informational message to the user.
362 fn info_msg(&mut self, message: &str); 360 fn info_msg(&mut self, message: &str);
363 /// \[Linux extension] Prompts the user for a yes/no/maybe conditional. 361 /// \[Linux extension] Prompts the user for a yes/no/maybe conditional.
401 }; 399 };
402 } 400 }
403 401
404 impl<C: Conversation> SimpleConversation for C { 402 impl<C: Conversation> SimpleConversation for C {
405 conv_fn!(prompt(message: &str) -> String { QAndA }); 403 conv_fn!(prompt(message: &str) -> String { QAndA });
406 conv_fn!(masked_prompt(message: &str) -> SecureString { MaskedQAndA } ); 404 conv_fn!(masked_prompt(message: &str) -> String { MaskedQAndA } );
407 conv_fn!(error_msg(message: &str) { ErrorMsg }); 405 conv_fn!(error_msg(message: &str) { ErrorMsg });
408 conv_fn!(info_msg(message: &str) { InfoMsg }); 406 conv_fn!(info_msg(message: &str) { InfoMsg });
409 conv_fn!(radio_prompt(message: &str) -> String { RadioQAndA }); 407 conv_fn!(radio_prompt(message: &str) -> String { RadioQAndA });
410 conv_fn!(binary_prompt((data, data_type): (&[u8], u8)) -> BinaryData { BinaryQAndA }); 408 conv_fn!(binary_prompt((data, data_type): (&[u8], u8)) -> BinaryData { BinaryQAndA });
411 } 409 }
462 "what" => Ok("whatwhat".to_owned()), 460 "what" => Ok("whatwhat".to_owned()),
463 "give_err" => Err(ErrorCode::PermissionDenied), 461 "give_err" => Err(ErrorCode::PermissionDenied),
464 _ => panic!("unexpected prompt!"), 462 _ => panic!("unexpected prompt!"),
465 } 463 }
466 } 464 }
467 fn masked_prompt(&mut self, request: &str) -> Result<SecureString> { 465 fn masked_prompt(&mut self, request: &str) -> Result<String> {
468 assert_eq!("reveal", request); 466 assert_eq!("reveal", request);
469 Ok(SecureString::from("my secrets")) 467 Ok("my secrets".to_owned())
470 } 468 }
471 fn error_msg(&mut self, message: &str) { 469 fn error_msg(&mut self, message: &str) {
472 self.error_ran = true; 470 self.error_ran = true;
473 assert_eq!("whoopsie", message); 471 assert_eq!("whoopsie", message);
474 } 472 }
505 info.message(), 503 info.message(),
506 has_err.message(), 504 has_err.message(),
507 ]); 505 ]);
508 506
509 assert_eq!("whatwhat", what.answer().unwrap()); 507 assert_eq!("whatwhat", what.answer().unwrap());
510 assert_eq!(SecureString::from("my secrets"), pass.answer().unwrap()); 508 assert_eq!("my secrets", pass.answer().unwrap());
511 assert_eq!(Ok(()), err.answer()); 509 assert_eq!(Ok(()), err.answer());
512 assert_eq!(Ok(()), info.answer()); 510 assert_eq!(Ok(()), info.answer());
513 assert_eq!(ErrorCode::PermissionDenied, has_err.answer().unwrap_err()); 511 assert_eq!(ErrorCode::PermissionDenied, has_err.answer().unwrap_err());
514 assert!(tester.error_ran); 512 assert!(tester.error_ran);
515 assert!(tester.info_ran); 513 assert!(tester.info_ran);
570 } 568 }
571 569
572 let mut tester = MuxTester; 570 let mut tester = MuxTester;
573 571
574 assert_eq!("answer", tester.prompt("question").unwrap()); 572 assert_eq!("answer", tester.prompt("question").unwrap());
575 assert_eq!( 573 assert_eq!("open sesame", tester.masked_prompt("password!").unwrap());
576 SecureString::from("open sesame"),
577 tester.masked_prompt("password!").unwrap()
578 );
579 tester.error_msg("oh no"); 574 tester.error_msg("oh no");
580 tester.info_msg("let me tell you"); 575 tester.info_msg("let me tell you");
581 // Linux-PAM extensions. Always implemented, but separate for clarity. 576 // Linux-PAM extensions. Always implemented, but separate for clarity.
582 { 577 {
583 assert_eq!("yes", tester.radio_prompt("radio?").unwrap()); 578 assert_eq!("yes", tester.radio_prompt("radio?").unwrap());