Skip to main content

origin_secrets/
secret.rs

1use std::fmt;
2
3/// A secret value.
4///
5/// `Debug` and `Display` are redacted so a secret cannot leak into a log line by
6/// accident (see the logging rules in `origin-telemetry`). Reading the real value is
7/// deliberately verbose: [`Secret::expose`].
8#[derive(Clone, PartialEq, Eq)]
9pub struct Secret(String);
10
11impl Secret {
12    pub fn new(value: impl Into<String>) -> Self {
13        Self(value.into())
14    }
15
16    /// Read the underlying value. Every call site is a place to review.
17    pub fn expose(&self) -> &str {
18        &self.0
19    }
20
21    pub fn into_inner(self) -> String {
22        self.0
23    }
24}
25
26impl fmt::Debug for Secret {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        f.write_str("Secret(***)")
29    }
30}
31
32impl fmt::Display for Secret {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        f.write_str("***")
35    }
36}
37
38impl From<&str> for Secret {
39    fn from(value: &str) -> Self {
40        Self::new(value)
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn debug_output_never_contains_the_value() {
50        let secret = Secret::new("ghp_supersecret");
51        assert_eq!(format!("{secret:?}"), "Secret(***)");
52        assert_eq!(format!("{secret}"), "***");
53        assert_eq!(secret.expose(), "ghp_supersecret");
54    }
55}