use cellos_core::types::SecretView;
use zeroize::{Zeroize, ZeroizeOnDrop};
fn _assert_zeroize_on_drop<T: ZeroizeOnDrop>() {}
#[test]
fn secret_view_implements_zeroize_on_drop() {
_assert_zeroize_on_drop::<SecretView>();
}
#[test]
fn secret_view_value_is_zeroizing_string() {
let view = SecretView {
key: "API_TOKEN".to_string(),
value: zeroize::Zeroizing::new("super-secret-payload".to_string()),
};
assert_eq!(view.value.as_str(), "super-secret-payload");
assert_eq!(view.key, "API_TOKEN");
}
#[test]
fn secret_view_zeroize_wipes_inner_string_bytes() {
const SECRET: &str = "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
assert_eq!(SECRET.len(), 64);
let mut view = SecretView {
key: "API_TOKEN".to_string(),
value: zeroize::Zeroizing::new(SECRET.to_string()),
};
let inner: &String = &view.value;
let ptr: *const u8 = inner.as_ptr();
let len: usize = inner.len();
assert_eq!(len, 64, "inner String should hold the 64-byte secret");
let pre = unsafe { std::slice::from_raw_parts(ptr, len) };
assert!(
pre.iter().all(|&b| b == b'!'),
"pre-zeroize bytes should match the secret payload"
);
view.value.zeroize();
let post = unsafe { std::slice::from_raw_parts(ptr, len) };
assert!(
post.iter().all(|&b| b == 0),
"post-zeroize: every byte at the original heap location must be 0; \
got non-zero residue: {:?}",
post.iter().filter(|&&b| b != 0).count()
);
drop(view);
}
#[test]
fn secret_view_drop_zeroizes_via_derive() {
let mut view = SecretView {
key: "K".to_string(),
value: zeroize::Zeroizing::new("payload".to_string()),
};
view.value.zeroize();
drop(view);
}