Skip to main content

atlassian_cli_auth/
secret.rs

1use secrecy::{ExposeSecret, SecretString};
2use std::fmt;
3
4/// A wrapper around a secret token that prevents accidental exposure.
5/// Uses `secrecy::SecretString` to ensure tokens are never logged or displayed.
6/// Automatically zeroed on drop to prevent memory disclosure.
7pub struct SecretToken(SecretString);
8
9impl SecretToken {
10    /// Create a new SecretToken from a String.
11    pub fn new(token: String) -> Self {
12        Self(SecretString::from(token))
13    }
14
15    /// Expose the secret token for use in authentication.
16    /// This should only be called when actually using the token (e.g., in HTTP headers).
17    pub fn expose(&self) -> &str {
18        self.0.expose_secret()
19    }
20}
21
22impl fmt::Debug for SecretToken {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        write!(f, "SecretToken([REDACTED])")
25    }
26}
27
28impl Drop for SecretToken {
29    fn drop(&mut self) {
30        // Zeroizing is handled by secrecy::SecretString internally
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn test_secret_token_creation() {
40        let token = SecretToken::new("my-secret-token".to_string());
41        assert_eq!(token.expose(), "my-secret-token");
42    }
43
44    #[test]
45    fn test_secret_token_debug_redacted() {
46        let token = SecretToken::new("my-secret-token".to_string());
47        let debug_output = format!("{:?}", token);
48        assert_eq!(debug_output, "SecretToken([REDACTED])");
49        assert!(!debug_output.contains("my-secret-token"));
50    }
51
52    #[test]
53    fn test_secret_token_no_clone() {
54        // This test documents that SecretToken cannot be cloned,
55        // preventing accidental duplication of secrets in memory.
56        // Uncommenting the following line should cause a compilation error:
57        // let token = SecretToken::new("test".to_string());
58        // let _cloned = token.clone();
59    }
60}