pub use a3s_common::privacy::ClassificationRule;
pub use a3s_common::privacy::RedactionStrategy;
pub use a3s_common::privacy::SensitivityLevel;
pub use a3s_common::privacy::{default_classification_rules, default_dangerous_commands};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct FeatureToggles {
pub output_sanitizer: bool,
pub taint_tracking: bool,
pub tool_interceptor: bool,
pub injection_defense: bool,
}
impl Default for FeatureToggles {
fn default() -> Self {
Self {
output_sanitizer: true,
taint_tracking: true,
tool_interceptor: true,
injection_defense: true,
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SecurityConfig {
pub enabled: bool,
pub classification_rules: Vec<ClassificationRule>,
pub redaction_strategy: RedactionStrategy,
pub network_whitelist: Vec<String>,
pub dangerous_commands: Vec<String>,
pub features: FeatureToggles,
}
impl Default for SecurityConfig {
fn default() -> Self {
Self {
enabled: true,
classification_rules: default_classification_rules(),
redaction_strategy: RedactionStrategy::Remove,
network_whitelist: Vec::new(),
dangerous_commands: default_dangerous_commands(),
features: FeatureToggles::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sensitivity_level_ordering() {
assert!(SensitivityLevel::Public < SensitivityLevel::Normal);
assert!(SensitivityLevel::Normal < SensitivityLevel::Sensitive);
assert!(SensitivityLevel::Sensitive < SensitivityLevel::HighlySensitive);
}
#[test]
fn test_sensitivity_level_display() {
assert_eq!(SensitivityLevel::Public.to_string(), "Public");
assert_eq!(
SensitivityLevel::HighlySensitive.to_string(),
"HighlySensitive"
);
}
#[test]
fn test_default_config() {
let config = SecurityConfig::default();
assert!(config.enabled);
assert!(!config.classification_rules.is_empty());
assert_eq!(config.redaction_strategy, RedactionStrategy::Remove);
assert!(!config.dangerous_commands.is_empty());
assert!(config.features.output_sanitizer);
}
#[test]
fn test_default_classification_rules() {
let rules = default_classification_rules();
assert_eq!(rules.len(), 5);
let names: Vec<&str> = rules.iter().map(|r| r.name.as_str()).collect();
assert!(names.contains(&"credit_card"));
assert!(names.contains(&"ssn"));
assert!(names.contains(&"email"));
assert!(names.contains(&"phone"));
assert!(names.contains(&"api_key"));
}
#[test]
fn test_default_dangerous_commands() {
let commands = default_dangerous_commands();
assert!(!commands.is_empty());
assert!(commands.iter().any(|c| c.contains("rm")));
assert!(commands.iter().any(|c| c.contains("dd")));
}
#[test]
fn test_config_serialization_roundtrip() {
let config = SecurityConfig::default();
let json = serde_json::to_string(&config).unwrap();
let parsed: SecurityConfig = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.enabled, config.enabled);
assert_eq!(parsed.redaction_strategy, config.redaction_strategy);
assert_eq!(
parsed.classification_rules.len(),
config.classification_rules.len()
);
}
#[test]
fn test_redaction_strategy_serialization() {
let mask = RedactionStrategy::Mask;
let json = serde_json::to_string(&mask).unwrap();
assert_eq!(json, "\"Mask\"");
let remove = RedactionStrategy::Remove;
let json = serde_json::to_string(&remove).unwrap();
assert_eq!(json, "\"Remove\"");
let hash = RedactionStrategy::Hash;
let json = serde_json::to_string(&hash).unwrap();
assert_eq!(json, "\"Hash\"");
}
}