kindly_guard_server/neutralizer/
security_aware.rs1use crate::{
20 neutralizer::{NeutralizeResult, ThreatNeutralizer},
21 scanner::Threat,
22 security::{CommandSource, SecurityContext},
23};
24use anyhow::Result;
25use async_trait::async_trait;
26use std::sync::Arc;
27
28pub struct SecurityAwareNeutralizer {
30 inner: Arc<dyn ThreatNeutralizer>,
31 security_context: Arc<tokio::sync::RwLock<SecurityContext>>,
32}
33
34impl SecurityAwareNeutralizer {
35 pub fn new(
36 neutralizer: Arc<dyn ThreatNeutralizer>,
37 security_context: Arc<tokio::sync::RwLock<SecurityContext>>,
38 ) -> Self {
39 Self {
40 inner: neutralizer,
41 security_context,
42 }
43 }
44
45 pub fn with_new_context(
47 neutralizer: Arc<dyn ThreatNeutralizer>,
48 source: CommandSource,
49 enhanced_mode: bool,
50 neutralization_mode: crate::security::NeutralizationMode,
51 ) -> Self {
52 let context = SecurityContext::new(source)
53 .with_enhanced_mode(enhanced_mode)
54 .with_neutralization_mode(neutralization_mode);
55
56 Self {
57 inner: neutralizer,
58 security_context: Arc::new(tokio::sync::RwLock::new(context)),
59 }
60 }
61
62 pub async fn get_context(&self) -> SecurityContext {
64 (*self.security_context.read().await).clone()
65 }
66
67 pub async fn set_user(&self, user_id: String) {
69 let mut context = self.security_context.write().await;
70 context.user_id = Some(user_id);
71 }
72}
73
74#[async_trait]
75impl ThreatNeutralizer for SecurityAwareNeutralizer {
76 async fn neutralize(&self, threat: &Threat, content: &str) -> Result<NeutralizeResult> {
77 let should_neutralize = {
79 let context = self.security_context.read().await;
80 context.should_neutralize()
81 };
82
83 if !should_neutralize {
84 return Ok(NeutralizeResult {
86 action_taken: crate::neutralizer::NeutralizeAction::NoAction,
87 sanitized_content: None,
88 confidence_score: 1.0,
89 processing_time_us: 0,
90 correlation_data: None,
91 extracted_params: None,
92 });
93 }
94
95 tracing::info!(
97 "Attempting neutralization for threat {:?} in security context {}",
98 threat.threat_type,
99 self.security_context.read().await.request_id
100 );
101
102 let result = self.inner.neutralize(threat, content).await;
104
105 let mut context = self.security_context.write().await;
107 match &result {
108 Ok(_) => {
109 context.record_neutralization(true);
110 tracing::info!(
111 "Neutralization successful for request {}. Total neutralized: {}",
112 context.request_id,
113 context.neutralization.threats_neutralized
114 );
115 },
116 Err(e) => {
117 context.record_neutralization(false);
118 tracing::error!(
119 "Neutralization failed for request {}: {}. Total failures: {}",
120 context.request_id,
121 e,
122 context.neutralization.neutralization_failures
123 );
124 },
125 }
126
127 result
128 }
129
130 fn can_neutralize(&self, threat_type: &crate::scanner::ThreatType) -> bool {
131 self.inner.can_neutralize(threat_type)
132 }
133
134 fn get_capabilities(&self) -> crate::neutralizer::NeutralizerCapabilities {
135 self.inner.get_capabilities()
136 }
137}
138
139pub struct NeutralizationSecurityManager {
141 contexts: Arc<
142 tokio::sync::RwLock<
143 std::collections::HashMap<String, Arc<tokio::sync::RwLock<SecurityContext>>>,
144 >,
145 >,
146}
147
148impl Default for NeutralizationSecurityManager {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154impl NeutralizationSecurityManager {
155 pub fn new() -> Self {
156 Self {
157 contexts: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())),
158 }
159 }
160
161 pub async fn create_context(
163 &self,
164 session_id: &str,
165 source: CommandSource,
166 ) -> Arc<tokio::sync::RwLock<SecurityContext>> {
167 let context = Arc::new(tokio::sync::RwLock::new(SecurityContext::new(source)));
168
169 let mut contexts = self.contexts.write().await;
170 contexts.insert(session_id.to_string(), context.clone());
171
172 context
173 }
174
175 pub async fn get_context(
177 &self,
178 session_id: &str,
179 ) -> Option<Arc<tokio::sync::RwLock<SecurityContext>>> {
180 let contexts = self.contexts.read().await;
181 contexts.get(session_id).cloned()
182 }
183
184 pub async fn remove_context(&self, session_id: &str) {
186 let mut contexts = self.contexts.write().await;
187 contexts.remove(session_id);
188 }
189
190 pub async fn get_summary(&self) -> NeutralizationSecuritySummary {
192 let contexts = self.contexts.read().await;
193
194 let mut total_neutralized = 0u32;
195 let mut total_failures = 0u32;
196 let mut active_sessions = 0usize;
197
198 for (_, context) in contexts.iter() {
199 let ctx = context.read().await;
200 total_neutralized += ctx.neutralization.threats_neutralized;
201 total_failures += ctx.neutralization.neutralization_failures;
202 active_sessions += 1;
203 }
204
205 NeutralizationSecuritySummary {
206 active_sessions,
207 total_neutralized,
208 total_failures,
209 overall_success_rate: if total_neutralized + total_failures > 0 {
210 f64::from(total_neutralized) / f64::from(total_neutralized + total_failures)
211 } else {
212 1.0
213 },
214 }
215 }
216}
217
218#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
220pub struct NeutralizationSecuritySummary {
221 pub active_sessions: usize,
222 pub total_neutralized: u32,
223 pub total_failures: u32,
224 pub overall_success_rate: f64,
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use crate::neutralizer::standard::StandardNeutralizer;
231 use crate::neutralizer::NeutralizationConfig;
232
233 #[tokio::test]
234 async fn test_security_aware_neutralizer() {
235 let config = NeutralizationConfig::default();
236 let neutralizer = Arc::new(StandardNeutralizer::new(config));
237
238 let security_neutralizer = SecurityAwareNeutralizer::with_new_context(
239 neutralizer,
240 CommandSource::Api,
241 false,
242 crate::security::NeutralizationMode::Automatic,
243 );
244
245 let threat = crate::scanner::Threat {
247 threat_type: crate::scanner::ThreatType::SqlInjection,
248 severity: crate::scanner::Severity::High,
249 location: crate::scanner::Location::Text {
250 offset: 0,
251 length: 10,
252 },
253 description: "SQL injection detected".to_string(),
254 remediation: None,
255 };
256
257 let result = security_neutralizer
258 .neutralize(&threat, "test content")
259 .await;
260 assert!(result.is_ok());
261
262 let context = security_neutralizer.get_context().await;
264 assert_eq!(context.neutralization.threats_neutralized, 1);
265 }
266}