use serde::Deserialize;
use serde::Serialize;
#[derive(Clone, Debug, Deserialize, Serialize, schemars::JsonSchema)]
#[serde(default, deny_unknown_fields)]
pub(crate) struct HeaderMaskingConfig {
pub(crate) enabled: bool,
pub(crate) sensitive_headers: Vec<String>,
pub(crate) replace_defaults: bool,
}
impl Default for HeaderMaskingConfig {
fn default() -> Self {
Self {
enabled: default_enabled(),
sensitive_headers: Vec::new(),
replace_defaults: false,
}
}
}
impl HeaderMaskingConfig {
pub(crate) fn effective_sensitive_headers(&self) -> Vec<String> {
if self.replace_defaults {
self.sensitive_headers.clone()
} else {
let mut headers = default_sensitive_headers();
headers.extend(self.sensitive_headers.iter().cloned());
headers
}
}
}
fn default_enabled() -> bool {
true
}
pub(crate) fn default_sensitive_headers() -> Vec<String> {
vec![
"authorization".to_string(),
"proxy-authorization".to_string(),
"proxy-authenticate".to_string(),
"www-authenticate".to_string(),
"cookie".to_string(),
"set-cookie".to_string(),
"x-api-key".to_string(),
"api-key".to_string(),
"x-auth-token".to_string(),
"x-session-id".to_string(),
"x-session-token".to_string(),
"x-csrf-token".to_string(),
"x-xsrf-token".to_string(),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = HeaderMaskingConfig::default();
assert!(config.enabled);
assert!(config.sensitive_headers.is_empty());
assert!(!config.replace_defaults);
let effective = config.effective_sensitive_headers();
assert!(effective.contains(&"authorization".to_string()));
assert!(effective.contains(&"cookie".to_string()));
assert!(effective.contains(&"x-api-key".to_string()));
}
#[test]
fn effective_sensitive_headers_merges_user_list_with_defaults() {
let config = HeaderMaskingConfig {
enabled: true,
sensitive_headers: vec!["x-my-secret".to_string()],
replace_defaults: false,
};
let effective = config.effective_sensitive_headers();
assert!(effective.contains(&"authorization".to_string()));
assert!(effective.contains(&"cookie".to_string()));
assert!(effective.contains(&"x-my-secret".to_string()));
}
#[test]
fn effective_sensitive_headers_honors_replace_defaults() {
let config = HeaderMaskingConfig {
enabled: true,
sensitive_headers: vec!["x-only-this".to_string()],
replace_defaults: true,
};
let effective = config.effective_sensitive_headers();
assert_eq!(effective, vec!["x-only-this".to_string()]);
}
#[test]
fn test_custom_config() {
let yaml = r#"
enabled: false
sensitive_headers:
- custom-secret
- x-internal-token
"#;
let config: HeaderMaskingConfig = serde_yaml::from_str(yaml).unwrap();
assert!(!config.enabled);
assert_eq!(config.sensitive_headers.len(), 2);
assert!(
config
.sensitive_headers
.contains(&"custom-secret".to_string())
);
assert!(
config
.sensitive_headers
.contains(&"x-internal-token".to_string())
);
}
#[test]
fn test_partial_config() {
let yaml = r#"
enabled: false
"#;
let config: HeaderMaskingConfig = serde_yaml::from_str(yaml).unwrap();
assert!(!config.enabled);
assert!(config.sensitive_headers.is_empty());
assert!(!config.replace_defaults);
}
}