Skip to main content

max / synckit

Test SecretToken's zeroize-on-drop through a callable method Drop delegated straight to zeroize, so mutation testing could replace its body with () and nothing failed. Move the scrub into zeroize_in_place, have Drop call it, and assert the buffer is zero after it runs.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-23 20:50 UTC
Signed with PGP, not checked
Commit: be92e3abe22bdb0b18c188965fab1197c8c208d6
Parent: 8c4ab94
1 file changed, +31 insertions, -2 deletions
@@ -332,6 +332,15 @@
332 332 pub fn as_str(&self) -> &str {
333 333 &self.0
334 334 }
335 +
336 + /// Scrub the token bytes in place, leaving an empty string behind.
337 + ///
338 + /// [`Drop`] delegates here rather than zeroizing inline so the guarantee is
339 + /// reachable from a test. Nothing else calls it.
340 + fn zeroize_in_place(&mut self) {
341 + use zeroize::Zeroize;
342 + self.0.zeroize();
343 + }
335 344 }
336 345
337 346 impl std::ops::Deref for SecretToken {
@@ -355,8 +364,7 @@
355 364
356 365 impl Drop for SecretToken {
357 366 fn drop(&mut self) {
358 - use zeroize::Zeroize;
359 - self.0.zeroize();
367 + self.zeroize_in_place();
360 368 }
361 369 }
362 370
@@ -723,6 +731,27 @@
723 731 assert!(version.starts_with(char::is_numeric));
724 732 }
725 733
734 + #[test]
735 + fn secret_token_zeroize_scrubs_the_buffer() {
736 + // The type exists to keep a bearer token out of memory after use, and
737 + // nothing else in the suite exercises that. Drop delegates to
738 + // zeroize_in_place, so calling it directly checks the same code path.
739 + let mut token = SecretToken::new("super-secret-jwt-value".to_string());
740 + let len = token.as_str().len();
741 + assert!(len > 0);
742 +
743 + token.zeroize_in_place();
744 +
745 + assert_eq!(token.as_str(), "");
746 + // zeroize keeps the String's allocation, so the bytes the token occupied
747 + // are still readable and must all be zero.
748 + let bytes = unsafe { std::slice::from_raw_parts(token.0.as_ptr(), len) };
749 + assert!(
750 + bytes.iter().all(|b| *b == 0),
751 + "token bytes survived the zeroize: {bytes:?}"
752 + );
753 + }
754 +
726 755 #[test]
727 756 fn requires_https_enforces_tls_off_loopback() {
728 757 // https or non-loopback http must enforce TLS.