kindly_guard_server/neutralizer/mod.rs
1// Copyright 2025 Kindly Software Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//! Threat neutralization system
15//!
16//! Provides actual threat remediation capabilities beyond just detection.
17//! Both standard and enhanced implementations provide full protection,
18//! with optimized implementations offering superior performance.
19
20use anyhow::Result;
21use async_trait::async_trait;
22use serde::{Deserialize, Serialize};
23use std::fmt;
24use std::sync::Arc;
25
26use crate::scanner::{Threat, ThreatType};
27
28pub mod api;
29#[cfg(feature = "enhanced")]
30pub mod enhanced;
31pub mod health;
32pub mod metrics;
33pub mod rate_limited;
34pub mod recovery;
35pub mod rollback;
36pub mod security_aware;
37pub mod standard;
38pub mod traced;
39pub mod validation;
40
41#[cfg(test)]
42mod security_tests;
43
44/// Trait for threat neutralization
45#[async_trait]
46pub trait ThreatNeutralizer: Send + Sync {
47 /// Neutralize a specific threat in content
48 async fn neutralize(&self, threat: &Threat, content: &str) -> Result<NeutralizeResult>;
49
50 /// Check if this neutralizer can handle a threat type
51 fn can_neutralize(&self, threat_type: &ThreatType) -> bool;
52
53 /// Get neutralizer capabilities
54 fn get_capabilities(&self) -> NeutralizerCapabilities;
55
56 /// Batch neutralize multiple threats
57 async fn batch_neutralize(
58 &self,
59 threats: &[Threat],
60 content: &str,
61 ) -> Result<BatchNeutralizeResult> {
62 let mut results = Vec::new();
63 let mut current_content = content.to_string();
64
65 for threat in threats {
66 let result = self.neutralize(threat, ¤t_content).await?;
67 if let Some(ref sanitized) = result.sanitized_content {
68 current_content = sanitized.clone();
69 }
70 results.push(result);
71 }
72
73 Ok(BatchNeutralizeResult {
74 final_content: current_content,
75 individual_results: results,
76 })
77 }
78}
79
80/// Result of neutralization operation
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct NeutralizeResult {
83 /// Action taken to neutralize threat
84 pub action_taken: NeutralizeAction,
85
86 /// Sanitized content (if modified)
87 pub sanitized_content: Option<String>,
88
89 /// Confidence in neutralization (0.0 - 1.0)
90 pub confidence_score: f64,
91
92 /// Processing time in microseconds
93 pub processing_time_us: u64,
94
95 /// Correlation data (enhanced mode only)
96 pub correlation_data: Option<CorrelationData>,
97
98 /// Any parameters extracted (e.g., SQL params)
99 pub extracted_params: Option<Vec<String>>,
100}
101
102/// Batch neutralization result
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct BatchNeutralizeResult {
105 /// Final sanitized content after all neutralizations
106 pub final_content: String,
107
108 /// Individual results for each threat
109 pub individual_results: Vec<NeutralizeResult>,
110}
111
112/// Actions that can be taken to neutralize threats
113///
114/// Each action represents a specific remediation technique applied
115/// to neutralize a detected security threat. The choice of action
116/// depends on the threat type and configured neutralization strategy.
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
118pub enum NeutralizeAction {
119 /// Content was sanitized by removing or modifying dangerous elements
120 ///
121 /// **When Used**: HTML/script content, user input with mixed safe/unsafe content
122 /// **Technique**: Removes dangerous tags/attributes while preserving safe content
123 /// **Example**: `<script>alert('XSS')</script>Hello` → `Hello`
124 /// **Preserves**: Safe text and allowed HTML tags
125 Sanitized,
126
127 /// Query was converted to use parameterized/prepared statements
128 ///
129 /// **When Used**: SQL, LDAP, or other injection attempts in queries
130 /// **Technique**: Separates query structure from user data
131 /// **Example**: `SELECT * FROM users WHERE id = '1' OR '1'='1'` → `SELECT * FROM users WHERE id = ?` with param `[1' OR '1'='1]`
132 /// **Preserves**: Query intent while preventing injection
133 Parameterized,
134
135 /// Path was normalized to prevent directory traversal
136 ///
137 /// **When Used**: File paths containing `../`, absolute paths, or other traversal attempts
138 /// **Technique**: Resolves to canonical path within allowed directory
139 /// **Example**: `/var/www/../../../etc/passwd` → `/etc/passwd` (blocked) or `/var/www/passwd` (allowed)
140 /// **Preserves**: Valid file references within boundaries
141 Normalized,
142
143 /// Content was escaped to prevent interpretation as code
144 ///
145 /// **When Used**: When content must be preserved but made safe for output context
146 /// **Technique**: Context-specific escaping (HTML, SQL, Shell, etc.)
147 /// **Example**: `<script>` → `<script>` (HTML context)
148 /// **Preserves**: Original content in escaped form
149 Escaped,
150
151 /// Threat was completely removed from content
152 ///
153 /// **When Used**: Malicious content with no legitimate use case
154 /// **Technique**: Strips out entire threat leaving remaining content
155 /// **Example**: `Hello[INVISIBLE_CHAR]World` → `HelloWorld`
156 /// **Preserves**: Only safe surrounding content
157 Removed,
158
159 /// Content was quarantined for manual review
160 ///
161 /// **When Used**: High-risk content requiring human judgment
162 /// **Technique**: Isolates content and blocks processing
163 /// **Example**: Suspected malware upload → moved to quarantine directory
164 /// **Preserves**: Original content in isolated storage
165 Quarantined,
166
167 /// No action was needed for this threat
168 ///
169 /// **When Used**: False positives, acceptable risks, or report-only mode
170 /// **Technique**: Threat logged but content unchanged
171 /// **Example**: Legitimate use of SQL keywords in documentation
172 /// **Preserves**: Everything unchanged
173 NoAction,
174}
175
176impl fmt::Display for NeutralizeAction {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 match self {
179 Self::Sanitized => write!(f, "Sanitized"),
180 Self::Parameterized => write!(f, "Parameterized"),
181 Self::Normalized => write!(f, "Normalized"),
182 Self::Escaped => write!(f, "Escaped"),
183 Self::Removed => write!(f, "Removed"),
184 Self::Quarantined => write!(f, "Quarantined"),
185 Self::NoAction => write!(f, "No Action"),
186 }
187 }
188}
189
190/// Correlation data from enhanced analysis
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct CorrelationData {
193 /// Related threat IDs
194 pub related_threats: Vec<String>,
195
196 /// Detected attack pattern
197 pub attack_pattern: Option<AttackPattern>,
198
199 /// Prediction confidence (0.0 - 1.0)
200 pub prediction_score: f64,
201}
202
203/// Attack patterns detected through correlation
204///
205/// These patterns represent coordinated or sophisticated attack behaviors
206/// identified by analyzing multiple threats in context. Detection of these
207/// patterns indicates a more serious security event requiring escalated response.
208#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
209pub enum AttackPattern {
210 /// Multiple unicode attacks in sequence indicating targeted deception
211 ///
212 /// **Detection**: Series of unicode-based attacks (homograph, BiDi, invisible chars)
213 /// **Implication**: Sophisticated attacker attempting various unicode bypasses
214 /// **Common Scenario**: Phishing campaigns, social engineering attempts
215 /// **Recommended Response**: Block source, enhanced monitoring, alert security team
216 CoordinatedUnicode,
217
218 /// SQL injection attempts across multiple inputs indicating database compromise attempt
219 ///
220 /// **Detection**: Multiple SQL injection variants targeting different parameters
221 /// **Implication**: Automated tool usage or skilled manual testing
222 /// **Common Scenario**: Database enumeration, data exfiltration attempts
223 /// **Recommended Response**: Enable WAF rules, review database access logs, consider IP blocking
224 SqlInjectionCampaign,
225
226 /// Command injection with privilege escalation attempts
227 ///
228 /// **Detection**: Command injections followed by privilege escalation patterns
229 /// **Implication**: Attacker seeking system-level access
230 /// **Common Scenario**: Webshell installation, backdoor creation
231 /// **Recommended Response**: Immediate incident response, system isolation, forensic analysis
232 CommandEscalation,
233
234 /// Mixed attack types indicating advanced persistent threat
235 ///
236 /// **Detection**: Combination of different attack vectors (XSS + SQLi + Path Traversal)
237 /// **Implication**: Sophisticated attacker using multiple techniques
238 /// **Common Scenario**: APT groups, professional penetration testing
239 /// **Recommended Response**: Full security audit, enhanced monitoring across all systems
240 MultiVector,
241
242 /// Reconnaissance pattern indicating pre-attack information gathering
243 ///
244 /// **Detection**: Low-severity probes, error triggering, boundary testing
245 /// **Implication**: Attack preparation phase, vulnerability scanning
246 /// **Common Scenario**: Automated scanning tools, manual reconnaissance
247 /// **Recommended Response**: Increase monitoring sensitivity, prepare incident response
248 Probing,
249}
250
251/// Neutralizer capabilities
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct NeutralizerCapabilities {
254 /// Can neutralize in real-time
255 pub real_time: bool,
256
257 /// Supports batch operations
258 pub batch_mode: bool,
259
260 /// Can predict future threats
261 pub predictive: bool,
262
263 /// Supports cross-threat correlation
264 pub correlation: bool,
265
266 /// Maximum rollback depth
267 pub rollback_depth: usize,
268
269 /// Supported threat types
270 pub supported_threats: Vec<ThreatType>,
271}
272
273/// Neutralization mode configuration
274///
275/// Determines how the neutralizer responds to detected threats.
276/// This allows flexible deployment from monitoring to active protection.
277#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
278pub enum NeutralizationMode {
279 /// Only report threats without modifying content
280 ///
281 /// **Use Case**: Initial deployment, testing, compliance monitoring
282 /// **Behavior**: Logs all threats but returns original content unchanged
283 /// **Security Impact**: No protection, only visibility
284 /// **Recommended For**: Pre-production testing, false positive analysis
285 ReportOnly,
286
287 /// Ask user for confirmation before neutralizing each threat
288 ///
289 /// **Use Case**: Semi-automated workflows, high-value content
290 /// **Behavior**: Prompts for user decision on each threat
291 /// **Security Impact**: Delayed protection, requires human availability
292 /// **Recommended For**: Content management systems, editorial workflows
293 Interactive,
294
295 /// Automatically neutralize all detected threats
296 ///
297 /// **Use Case**: Production systems, real-time protection
298 /// **Behavior**: Immediately applies configured neutralization actions
299 /// **Security Impact**: Maximum protection, potential for false positives
300 /// **Recommended For**: API gateways, web applications, automated systems
301 Automatic,
302}
303
304impl Default for NeutralizationMode {
305 fn default() -> Self {
306 Self::ReportOnly
307 }
308}
309
310/// Configuration for neutralization
311///
312/// # Security Implications
313///
314/// Neutralization transforms detected threats into safe content:
315/// - **Mode selection** - Balance between security and data integrity
316/// - **Backup strategy** - Enables recovery but requires secure storage
317/// - **Audit requirements** - Essential for compliance and forensics
318/// - **Recovery handling** - Prevents neutralization failures from causing outages
319///
320/// # Example: Secure Production Configuration
321///
322/// ```toml
323/// [neutralization]
324/// mode = "automatic" # Auto-neutralize threats
325/// backup_originals = true # Keep originals for recovery
326/// audit_all_actions = true # Full audit trail
327///
328/// [neutralization.unicode]
329/// bidi_replacement = "marker" # Visible markers for BiDi
330/// zero_width_action = "remove" # Remove invisible chars
331/// homograph_action = "ascii" # Convert to ASCII
332///
333/// [neutralization.injection]
334/// sql_action = "parameterize" # Convert to safe queries
335/// command_action = "escape" # Escape shell metacharacters
336/// path_action = "normalize" # Resolve to safe paths
337/// prompt_action = "wrap" # Add safety boundaries
338///
339/// [neutralization.recovery]
340/// enabled = true
341/// max_retries = 3
342/// backoff_ms = 100
343/// ```
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct NeutralizationConfig {
346 /// Neutralization mode
347 ///
348 /// **Default**: ReportOnly (safe default)
349 /// **Security Trade-offs**:
350 /// - `ReportOnly`: Detects but doesn't modify (safe for testing)
351 /// - `Interactive`: Requires user confirmation (good for sensitive data)
352 /// - `Automatic`: Immediate protection (recommended for production)
353 pub mode: NeutralizationMode,
354
355 /// Backup original content
356 ///
357 /// **Default**: true (secure by default)
358 /// **Security**: Enables recovery from false positives but requires
359 /// secure storage. Backups should be encrypted and access-controlled.
360 /// **Warning**: Disabling prevents recovery from mistakes
361 pub backup_originals: bool,
362
363 /// Audit all actions
364 ///
365 /// **Default**: true (secure by default)
366 /// **Security**: Creates forensic trail for all neutralization actions.
367 /// Essential for compliance, debugging, and incident response.
368 /// **Storage**: Ensure audit logs are tamper-proof and retained properly
369 pub audit_all_actions: bool,
370
371 /// Unicode-specific settings
372 ///
373 /// **Security**: Controls how unicode-based threats are neutralized.
374 /// Different strategies balance security vs. internationalization needs.
375 pub unicode: UnicodeNeutralizationConfig,
376
377 /// Injection-specific settings
378 ///
379 /// **Security**: Defines how various injection attacks are neutralized.
380 /// Each injection type requires specific handling to maintain functionality.
381 pub injection: InjectionNeutralizationConfig,
382
383 /// Recovery configuration for handling failures
384 ///
385 /// **Default**: Enabled with sensible retry settings
386 /// **Security**: Prevents neutralization failures from causing service outages.
387 /// Implements circuit breakers and exponential backoff.
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub recovery: Option<recovery::RecoveryConfig>,
390}
391
392/// Unicode neutralization configuration
393///
394/// Controls how unicode-based security threats are handled.
395/// These attacks exploit unicode features to deceive users or systems.
396#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct UnicodeNeutralizationConfig {
398 /// How to handle `BiDi` characters
399 ///
400 /// **Default**: Marker (visible indication)
401 /// **Security**: BiDi characters can reverse text direction to deceive users.
402 /// - `Remove`: Most secure, may break legitimate RTL text
403 /// - `Marker`: Balance of security and usability (recommended)
404 /// - `Escape`: Preserves data but may confuse users
405 pub bidi_replacement: BiDiReplacement,
406
407 /// Action for zero-width characters
408 ///
409 /// **Default**: Remove (most secure)
410 /// **Security**: Zero-width characters are invisible and used for:
411 /// - Hidden tracking codes
412 /// - Bypassing filters
413 /// - Creating invisible URLs
414 /// **Warning**: Some languages legitimately use zero-width joiners
415 pub zero_width_action: ZeroWidthAction,
416
417 /// Action for homographs
418 ///
419 /// **Default**: Ascii (convert lookalikes)
420 /// **Security**: Homographs look like ASCII but aren't (е vs e).
421 /// - `Ascii`: Converts to ASCII equivalent (most secure)
422 /// - `Warn`: Alerts but preserves (for international apps)
423 /// - `Block`: Rejects content entirely (strictest)
424 pub homograph_action: HomographAction,
425}
426
427/// `BiDi` character replacement strategy
428#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
429pub enum BiDiReplacement {
430 /// Remove completely
431 Remove,
432
433 /// Replace with visible marker
434 Marker,
435
436 /// Escape as unicode sequence
437 Escape,
438}
439
440/// Zero-width character action
441#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
442pub enum ZeroWidthAction {
443 /// Remove completely
444 Remove,
445
446 /// Escape as unicode sequence
447 Escape,
448}
449
450/// Homograph character action
451#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
452pub enum HomographAction {
453 /// Convert to ASCII equivalent
454 Ascii,
455
456 /// Warn but keep
457 Warn,
458
459 /// Block completely
460 Block,
461}
462
463/// Injection neutralization configuration
464///
465/// Controls how various injection attacks are neutralized.
466/// Each injection type requires specific handling to maintain functionality
467/// while ensuring security.
468#[derive(Debug, Clone, Serialize, Deserialize)]
469pub struct InjectionNeutralizationConfig {
470 /// SQL injection action
471 ///
472 /// **Default**: Parameterize (most secure)
473 /// **Security**: SQL injection can lead to data breaches and corruption.
474 /// - `Block`: Rejects query entirely (safest but may break functionality)
475 /// - `Escape`: Escapes dangerous characters (good but not foolproof)
476 /// - `Parameterize`: Converts to prepared statements (recommended)
477 pub sql_action: SqlAction,
478
479 /// Command injection action
480 ///
481 /// **Default**: Escape (balanced approach)
482 /// **Security**: Command injection enables arbitrary code execution.
483 /// - `Block`: Rejects command entirely (safest)
484 /// - `Escape`: Escapes shell metacharacters (recommended)
485 /// - `Sandbox`: Runs in restricted environment (complex but safe)
486 pub command_action: CommandAction,
487
488 /// Path traversal action
489 ///
490 /// **Default**: Normalize (maintains functionality)
491 /// **Security**: Path traversal accesses unauthorized files.
492 /// - `Block`: Rejects paths with traversal patterns
493 /// - `Normalize`: Resolves to canonical safe path (recommended)
494 pub path_action: PathAction,
495
496 /// Prompt injection action
497 ///
498 /// **Default**: Wrap (adds safety context)
499 /// **Security**: Prompt injection manipulates AI behavior.
500 /// - `Block`: Rejects suspicious prompts
501 /// - `Escape`: Escapes control sequences
502 /// - `Wrap`: Adds safety boundaries (recommended for LLMs)
503 pub prompt_action: PromptAction,
504}
505
506/// SQL injection neutralization action
507#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
508pub enum SqlAction {
509 /// Block the query
510 Block,
511
512 /// Escape dangerous characters
513 Escape,
514
515 /// Convert to parameterized query
516 Parameterize,
517}
518
519/// Command injection neutralization action
520#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
521pub enum CommandAction {
522 /// Block the command
523 Block,
524
525 /// Escape shell metacharacters
526 Escape,
527
528 /// Sandbox the command
529 Sandbox,
530}
531
532/// Path traversal neutralization action
533#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
534pub enum PathAction {
535 /// Block the path
536 Block,
537
538 /// Normalize to safe path
539 Normalize,
540}
541
542/// Prompt injection neutralization action
543#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
544pub enum PromptAction {
545 /// Block the prompt
546 Block,
547
548 /// Escape control sequences
549 Escape,
550
551 /// Wrap in safety context
552 Wrap,
553}
554
555impl Default for NeutralizationConfig {
556 fn default() -> Self {
557 Self {
558 mode: NeutralizationMode::default(),
559 backup_originals: true,
560 audit_all_actions: true,
561 unicode: UnicodeNeutralizationConfig {
562 bidi_replacement: BiDiReplacement::Marker,
563 zero_width_action: ZeroWidthAction::Remove,
564 homograph_action: HomographAction::Ascii,
565 },
566 injection: InjectionNeutralizationConfig {
567 sql_action: SqlAction::Parameterize,
568 command_action: CommandAction::Escape,
569 path_action: PathAction::Normalize,
570 prompt_action: PromptAction::Wrap,
571 },
572 recovery: Some(recovery::RecoveryConfig::default()),
573 }
574 }
575}
576
577/// Factory for creating neutralizers
578pub fn create_neutralizer(
579 config: &NeutralizationConfig,
580 rate_limiter: Option<Arc<dyn crate::traits::RateLimiter>>,
581) -> Arc<dyn ThreatNeutralizer> {
582 create_neutralizer_with_telemetry(config, rate_limiter, None)
583}
584
585/// Factory for creating neutralizers with optional telemetry
586pub fn create_neutralizer_with_telemetry(
587 config: &NeutralizationConfig,
588 rate_limiter: Option<Arc<dyn crate::traits::RateLimiter>>,
589 tracing_provider: Option<Arc<crate::telemetry::DistributedTracingProvider>>,
590) -> Arc<dyn ThreatNeutralizer> {
591 // Create base neutralizer
592 let mut neutralizer: Arc<dyn ThreatNeutralizer> = {
593 #[cfg(feature = "enhanced")]
594 {
595 Arc::new(enhanced::EnhancedNeutralizer::new(config.clone()))
596 }
597
598 #[cfg(not(feature = "enhanced"))]
599 {
600 Arc::new(standard::StandardNeutralizer::new(config.clone()))
601 }
602 };
603
604 // Optionally wrap with recovery
605 if let Some(ref recovery_config) = config.recovery {
606 if recovery_config.enabled {
607 neutralizer = Arc::new(recovery::ResilientNeutralizer::new(
608 neutralizer,
609 recovery_config.clone(),
610 ));
611 }
612 }
613
614 // Optionally wrap with rollback support
615 if config.backup_originals {
616 neutralizer =
617 rollback::RollbackNeutralizer::new(neutralizer, rollback::RollbackConfig::default());
618 }
619
620 // Optionally wrap with rate limiting
621 if let Some(limiter) = rate_limiter {
622 neutralizer = Arc::new(rate_limited::RateLimitedNeutralizer::new(
623 neutralizer,
624 limiter,
625 rate_limited::NeutralizationRateLimitConfig::default(),
626 ));
627 }
628
629 // Always wrap with health monitoring
630 neutralizer = health::HealthMonitoredNeutralizer::new(
631 neutralizer,
632 health::NeutralizationHealthConfig::default(),
633 );
634
635 // Optionally wrap with distributed tracing
636 if let Some(provider) = tracing_provider {
637 use crate::neutralizer::traced::NeutralizerTracingExt;
638 neutralizer = neutralizer.with_tracing(provider);
639 }
640
641 neutralizer
642}
643
644/// Neutralization error types
645#[derive(Debug, thiserror::Error)]
646pub enum NeutralizeError {
647 #[error("Threat type not supported: {0:?}")]
648 UnsupportedThreatType(ThreatType),
649
650 #[error("Neutralization failed: {0}")]
651 NeutralizationFailed(String),
652
653 #[error("Invalid configuration: {0}")]
654 InvalidConfig(String),
655}