Skip to main content

cc_audit/proxy/
config.rs

1//! Proxy configuration.
2
3use std::net::SocketAddr;
4use std::path::PathBuf;
5
6/// Configuration for the proxy server.
7#[derive(Debug, Clone)]
8pub struct ProxyConfig {
9    /// Address to listen on
10    pub listen_addr: SocketAddr,
11
12    /// Target MCP server address
13    pub target_addr: SocketAddr,
14
15    /// Enable TLS termination
16    pub tls_enabled: bool,
17
18    /// TLS certificate file (optional, will generate self-signed if not provided)
19    pub tls_cert_file: Option<PathBuf>,
20
21    /// TLS key file (optional)
22    pub tls_key_file: Option<PathBuf>,
23
24    /// Block mode: if true, block messages with findings; if false, log only
25    pub block_mode: bool,
26
27    /// Log file path for JSONL output
28    pub log_file: Option<PathBuf>,
29
30    /// Minimum severity to trigger blocking (when block_mode is true)
31    pub min_block_severity: crate::Severity,
32
33    /// Verbose logging
34    pub verbose: bool,
35}
36
37impl Default for ProxyConfig {
38    fn default() -> Self {
39        Self {
40            listen_addr: "127.0.0.1:8080".parse().unwrap(),
41            target_addr: "127.0.0.1:3000".parse().unwrap(),
42            tls_enabled: false,
43            tls_cert_file: None,
44            tls_key_file: None,
45            block_mode: false,
46            log_file: None,
47            min_block_severity: crate::Severity::High,
48            verbose: false,
49        }
50    }
51}
52
53impl ProxyConfig {
54    /// Create a new proxy config with the given listen and target addresses.
55    pub fn new(listen_addr: SocketAddr, target_addr: SocketAddr) -> Self {
56        Self {
57            listen_addr,
58            target_addr,
59            ..Default::default()
60        }
61    }
62
63    /// Enable TLS with auto-generated self-signed certificate.
64    pub fn with_tls(mut self) -> Self {
65        self.tls_enabled = true;
66        self
67    }
68
69    /// Enable TLS with custom certificate files.
70    pub fn with_tls_files(mut self, cert: PathBuf, key: PathBuf) -> Self {
71        self.tls_enabled = true;
72        self.tls_cert_file = Some(cert);
73        self.tls_key_file = Some(key);
74        self
75    }
76
77    /// Enable block mode.
78    pub fn with_block_mode(mut self, min_severity: crate::Severity) -> Self {
79        self.block_mode = true;
80        self.min_block_severity = min_severity;
81        self
82    }
83
84    /// Set log file path.
85    pub fn with_log_file(mut self, path: PathBuf) -> Self {
86        self.log_file = Some(path);
87        self
88    }
89
90    /// Enable verbose logging.
91    pub fn with_verbose(mut self) -> Self {
92        self.verbose = true;
93        self
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    use std::path::PathBuf;
101
102    #[test]
103    fn test_default_config() {
104        let config = ProxyConfig::default();
105        assert_eq!(config.listen_addr.port(), 8080);
106        assert!(!config.tls_enabled);
107        assert!(!config.block_mode);
108    }
109
110    #[test]
111    fn test_config_builder() {
112        let config = ProxyConfig::new(
113            "0.0.0.0:9000".parse().unwrap(),
114            "127.0.0.1:3000".parse().unwrap(),
115        )
116        .with_tls()
117        .with_block_mode(crate::Severity::Critical)
118        .with_verbose();
119
120        assert_eq!(config.listen_addr.port(), 9000);
121        assert!(config.tls_enabled);
122        assert!(config.block_mode);
123        assert_eq!(config.min_block_severity, crate::Severity::Critical);
124        assert!(config.verbose);
125    }
126
127    #[test]
128    fn test_with_tls_files() {
129        let config = ProxyConfig::default().with_tls_files(
130            PathBuf::from("/path/to/cert.pem"),
131            PathBuf::from("/path/to/key.pem"),
132        );
133
134        assert!(config.tls_enabled);
135        assert_eq!(
136            config.tls_cert_file,
137            Some(PathBuf::from("/path/to/cert.pem"))
138        );
139        assert_eq!(config.tls_key_file, Some(PathBuf::from("/path/to/key.pem")));
140    }
141
142    #[test]
143    fn test_with_log_file() {
144        let config = ProxyConfig::default().with_log_file(PathBuf::from("/var/log/proxy.jsonl"));
145
146        assert_eq!(config.log_file, Some(PathBuf::from("/var/log/proxy.jsonl")));
147    }
148
149    #[test]
150    fn test_default_severity() {
151        let config = ProxyConfig::default();
152        assert_eq!(config.min_block_severity, crate::Severity::High);
153    }
154
155    #[test]
156    fn test_config_clone() {
157        let config = ProxyConfig::default()
158            .with_tls()
159            .with_block_mode(crate::Severity::Medium)
160            .with_verbose();
161
162        let cloned = config.clone();
163
164        assert_eq!(cloned.listen_addr, config.listen_addr);
165        assert_eq!(cloned.target_addr, config.target_addr);
166        assert_eq!(cloned.tls_enabled, config.tls_enabled);
167        assert_eq!(cloned.block_mode, config.block_mode);
168        assert_eq!(cloned.min_block_severity, config.min_block_severity);
169        assert_eq!(cloned.verbose, config.verbose);
170    }
171
172    #[test]
173    fn test_config_debug() {
174        let config = ProxyConfig::default();
175        let debug_str = format!("{:?}", config);
176
177        assert!(debug_str.contains("ProxyConfig"));
178        assert!(debug_str.contains("listen_addr"));
179        assert!(debug_str.contains("target_addr"));
180    }
181
182    #[test]
183    fn test_new_with_specific_addresses() {
184        let config = ProxyConfig::new(
185            "192.168.1.1:8888".parse().unwrap(),
186            "10.0.0.1:3333".parse().unwrap(),
187        );
188
189        assert_eq!(config.listen_addr.ip().to_string(), "192.168.1.1");
190        assert_eq!(config.listen_addr.port(), 8888);
191        assert_eq!(config.target_addr.ip().to_string(), "10.0.0.1");
192        assert_eq!(config.target_addr.port(), 3333);
193    }
194
195    #[test]
196    fn test_chained_builder() {
197        let log_path = PathBuf::from("/tmp/test.log");
198        let cert_path = PathBuf::from("/tmp/cert.pem");
199        let key_path = PathBuf::from("/tmp/key.pem");
200
201        let config = ProxyConfig::default()
202            .with_tls_files(cert_path.clone(), key_path.clone())
203            .with_block_mode(crate::Severity::Low)
204            .with_log_file(log_path.clone())
205            .with_verbose();
206
207        assert!(config.tls_enabled);
208        assert_eq!(config.tls_cert_file, Some(cert_path));
209        assert_eq!(config.tls_key_file, Some(key_path));
210        assert!(config.block_mode);
211        assert_eq!(config.min_block_severity, crate::Severity::Low);
212        assert_eq!(config.log_file, Some(log_path));
213        assert!(config.verbose);
214    }
215}