pub type SecretBytes = SecretSlice<u8>;
pub use secrecy::{ExposeSecret, ExposeSecretMut, SecretBox, SecretSlice, SecretString};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn secret_bytes_from_vec_and_expose_roundtrip() {
let raw = vec![1_u8, 2, 3, 4, 5];
let secret: SecretBytes = raw.clone().into();
assert_eq!(secret.expose_secret(), raw.as_slice());
}
#[test]
fn debug_never_leaks_bytes() {
let secret: SecretBytes = vec![0xde_u8, 0xad, 0xbe, 0xef].into();
let debug = format!("{secret:?}");
let lower = debug.to_lowercase();
assert!(lower.contains("redacted"), "Debug 未标注脱敏:{debug}");
for leak in ["222", "173", "190", "239", "dead", "beef", "de ad"] {
assert!(
!lower.contains(leak),
"Debug 输出泄露明文痕迹 {leak:?}:{debug}"
);
}
}
#[test]
fn nested_in_derived_debug_still_safe() {
#[expect(
dead_code,
reason = "字段仅用于构造 Debug 输出,不参与读取;这是本测试的全部意图"
)]
#[derive(Debug)]
struct Wrapper {
label: &'static str,
secret: SecretBytes,
}
let w = Wrapper {
label: "github",
secret: vec![0xde_u8, 0xad].into(),
};
let text = format!("{w:?}");
assert!(text.contains("github"), "非秘密字段应正常打印:{text}");
assert!(!text.contains("222"), "嵌套 Debug 泄露明文:{text}");
}
#[test]
fn zeroize_on_drop_contract_holds() {
fn assert_zeroize_on_drop<T: secrecy::zeroize::ZeroizeOnDrop>() {}
fn assert_zeroize<T: secrecy::zeroize::Zeroize>() {}
assert_zeroize_on_drop::<SecretBytes>();
assert_zeroize::<SecretBytes>();
assert_zeroize_on_drop::<SecretString>();
}
#[test]
fn constructible_from_owned_input() {
let bytes: SecretBytes = vec![0_u8; 32].into();
assert_eq!(bytes.expose_secret().len(), 32);
let text = SecretString::from("correct horse battery staple");
assert_eq!(text.expose_secret(), "correct horse battery staple");
}
#[test]
fn secret_bytes_is_cloneable_and_clone_exposes_same() {
let secret: SecretBytes = vec![9_u8, 8, 7].into();
let copy = secret.clone();
assert_eq!(copy.expose_secret(), secret.expose_secret());
assert_eq!(copy.expose_secret(), &[9, 8, 7]);
}
#[test]
fn container_secret_box_is_not_cloneable_by_upstream_design() {
let scalar = SecretBox::new(Box::new(0xAB_u8));
let copy = scalar.clone();
assert_eq!(*copy.expose_secret(), 0xAB);
}
}