Skip to main content

eggress_system_proxy/
redaction.rs

1use std::collections::HashMap;
2
3/// Redact sensitive information from proxy URIs and settings.
4///
5/// URI redaction is shared with `eggress-uri` so bracketed IPv6 endpoints and
6/// passwords containing `@` have one consistent implementation.
7pub use eggress_uri::redact_proxy_uri;
8
9/// Redact proxy settings map values.
10///
11/// Keys containing "proxy" (case-insensitive) have their values
12/// processed through `redact_proxy_uri`.
13pub fn redact_proxy_settings(settings: &HashMap<String, String>) -> HashMap<String, String> {
14    settings
15        .iter()
16        .map(|(k, v)| {
17            if k.to_lowercase().contains("proxy") {
18                (k.clone(), redact_proxy_uri(v))
19            } else {
20                (k.clone(), v.clone())
21            }
22        })
23        .collect()
24}
25
26/// Redact a list of proxy URIs.
27pub fn redact_proxy_uris(uris: &[String]) -> Vec<String> {
28    uris.iter().map(|u| redact_proxy_uri(u)).collect()
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34
35    #[test]
36    fn redact_uri_with_credentials() {
37        assert_eq!(
38            redact_proxy_uri("http://user:secret@proxy.example.com:8080"),
39            "http://****@proxy.example.com:8080"
40        );
41    }
42
43    #[test]
44    fn redact_uri_without_credentials() {
45        assert_eq!(
46            redact_proxy_uri("http://proxy.example.com:8080"),
47            "http://proxy.example.com:8080"
48        );
49    }
50
51    #[test]
52    fn redact_uri_with_username_only() {
53        assert_eq!(
54            redact_proxy_uri("http://user@proxy.example.com:8080"),
55            "http://****@proxy.example.com:8080"
56        );
57    }
58
59    #[test]
60    fn redact_uri_socks_with_credentials() {
61        assert_eq!(
62            redact_proxy_uri("socks5://admin:password123@127.0.0.1:1080"),
63            "socks5://****@127.0.0.1:1080"
64        );
65    }
66
67    #[test]
68    fn redact_uri_no_at_sign() {
69        assert_eq!(
70            redact_proxy_uri("http://proxy.example.com:8080"),
71            "http://proxy.example.com:8080"
72        );
73    }
74
75    #[test]
76    fn redact_uri_handles_ipv6_and_at_signs_in_password() {
77        assert_eq!(
78            redact_proxy_uri("http://user:p@ss@[::1]:8080"),
79            "http://****@[::1]:8080"
80        );
81    }
82
83    #[test]
84    fn redact_settings_map() {
85        let mut settings = HashMap::new();
86        settings.insert(
87            "http_proxy".to_string(),
88            "http://user:pass@proxy:8080".to_string(),
89        );
90        settings.insert("no_proxy".to_string(), "localhost,127.0.0.1".to_string());
91        settings.insert(
92            "HTTP_PROXY".to_string(),
93            "http://admin:secret@proxy:8080".to_string(),
94        );
95
96        let redacted = redact_proxy_settings(&settings);
97        assert_eq!(
98            redacted.get("http_proxy").unwrap(),
99            "http://****@proxy:8080"
100        );
101        assert_eq!(redacted.get("no_proxy").unwrap(), "localhost,127.0.0.1");
102        assert_eq!(
103            redacted.get("HTTP_PROXY").unwrap(),
104            "http://****@proxy:8080"
105        );
106    }
107
108    #[test]
109    fn redact_uris_list() {
110        let uris = vec![
111            "http://user:secret@proxy:8080".to_string(),
112            "http://proxy:8080".to_string(),
113        ];
114        let redacted = redact_proxy_uris(&uris);
115        assert_eq!(redacted[0], "http://****@proxy:8080");
116        assert_eq!(redacted[1], "http://proxy:8080");
117    }
118}