id_contract.rs (3256B)
1 use radroots_secrets::error::SecretIdError; 2 use radroots_secrets::id::{BackendKind, KeyVersion, SECRET_ID_MAX_BYTES}; 3 use radroots_secrets::{Error, SecretId, SecretRef}; 4 5 #[test] 6 fn identifiers_accept_the_portable_alphabet_and_reject_unsafe_values() { 7 for valid in ["a", "account-01", "farm.primary_key:v2", "A_B"] { 8 assert_eq!(SecretId::parse(valid).expect("valid id").as_str(), valid); 9 } 10 11 assert_eq!( 12 SecretId::parse(""), 13 Err(Error::InvalidSecretId(SecretIdError::Empty)) 14 ); 15 assert_eq!( 16 SecretId::parse("-leading-separator"), 17 Err(Error::InvalidSecretId(SecretIdError::InvalidCharacter { 18 byte_offset: 0 19 })) 20 ); 21 assert_eq!( 22 SecretId::parse("path/traversal"), 23 Err(Error::InvalidSecretId(SecretIdError::InvalidCharacter { 24 byte_offset: 4 25 })) 26 ); 27 assert_eq!( 28 SecretId::parse("é"), 29 Err(Error::InvalidSecretId(SecretIdError::InvalidCharacter { 30 byte_offset: 0 31 })) 32 ); 33 assert_eq!( 34 SecretId::parse("a".repeat(SECRET_ID_MAX_BYTES + 1)), 35 Err(Error::InvalidSecretId(SecretIdError::TooLong { 36 actual_bytes: SECRET_ID_MAX_BYTES + 1, 37 max_bytes: SECRET_ID_MAX_BYTES, 38 })) 39 ); 40 } 41 42 #[test] 43 fn identifiers_and_references_are_redacted_in_diagnostics() { 44 let id = SecretId::parse("account-signing-key").expect("valid id"); 45 assert_eq!(format!("{id:?}"), "SecretId(<redacted>)"); 46 assert_eq!(id.to_string(), "<redacted secret id>"); 47 48 let reference = SecretRef::new( 49 id, 50 BackendKind::Keyring, 51 KeyVersion::new(7).expect("non-zero version"), 52 ); 53 let diagnostic = format!("{reference:?}"); 54 assert!(diagnostic.contains("<redacted>")); 55 assert!(diagnostic.contains("Keyring")); 56 assert!(diagnostic.contains("KeyVersion(7)")); 57 assert!(!diagnostic.contains("account-signing-key")); 58 } 59 60 #[test] 61 fn references_expose_only_validated_metadata() { 62 assert_eq!(KeyVersion::new(0), Err(Error::InvalidKeyVersion)); 63 let version = KeyVersion::new(3).expect("non-zero version"); 64 let reference = SecretRef::new( 65 SecretId::parse("service-token").expect("valid id"), 66 BackendKind::External, 67 version, 68 ); 69 70 assert_eq!(reference.id().as_str(), "service-token"); 71 assert_eq!(reference.backend(), BackendKind::External); 72 assert_eq!(reference.key_version().get(), 3); 73 } 74 75 #[cfg(feature = "serde")] 76 #[test] 77 fn identifier_serde_round_trips_through_validation() { 78 let id = SecretId::parse("farm.primary-key:v1").expect("valid id"); 79 let json = serde_json::to_string(&id).expect("serialize id"); 80 assert_eq!(json, "\"farm.primary-key:v1\""); 81 assert_eq!( 82 serde_json::from_str::<SecretId>(&json) 83 .expect("deserialize id") 84 .as_str(), 85 id.as_str() 86 ); 87 assert!(serde_json::from_str::<SecretId>("\"../escape\"").is_err()); 88 } 89 90 #[test] 91 fn normalized_errors_never_echo_identifier_input() { 92 let raw = "secret/value"; 93 let error = SecretId::parse(raw).expect_err("invalid id"); 94 let display = error.to_string(); 95 let debug = format!("{error:?}"); 96 assert!(!display.contains(raw)); 97 assert!(!debug.contains(raw)); 98 }