SecurityScanner

Struct SecurityScanner 

Source
pub struct SecurityScanner {
    pub patterns: ThreatPatterns,
    /* private fields */
}
Expand description

Main security scanner combining all threat detection

The SecurityScanner is the central component for detecting security threats in text and JSON inputs. It combines multiple specialized scanners (Unicode, Injection, XSS, Crypto) and supports plugin-based extensions.

§Architecture

  • Unicode Scanner: Detects invisible characters, BiDi spoofing, homograph attacks
  • Injection Scanner: Detects SQL, command, prompt, and other injection attempts
  • XSS Scanner: Detects cross-site scripting patterns
  • Crypto Scanner: Detects weak cryptographic patterns and insecure implementations
  • Plugin System: Allows custom threat detection via external plugins

§Security Considerations

  • All scanners run with configurable depth limits to prevent DoS attacks
  • Pattern matching uses size-limited regex to prevent ReDoS attacks
  • Results are type-safe using enums, never raw strings for security decisions
  • Enhanced mode provides additional correlation and pattern analysis

§Performance

  • Scanners use zero-copy operations where possible
  • Text is scanned in a single pass per scanner
  • JSON scanning uses recursive descent with depth limiting
  • Enhanced mode may use SIMD optimizations when available

Fields§

§patterns: ThreatPatterns

Implementations§

Source§

impl SecurityScanner

Source

pub fn set_plugin_manager( &mut self, plugin_manager: Arc<dyn PluginManagerTrait>, )

Set the plugin manager for this scanner

Enables plugin-based threat detection by attaching a plugin manager to the scanner. Plugins can detect custom threats specific to your application or domain.

§Arguments
  • plugin_manager - The plugin manager that will coordinate plugin scanning
§Plugin Security
  • Plugins run in isolated contexts with limited permissions
  • Plugin errors are logged but don’t fail the main scan
  • Each plugin has configurable timeouts to prevent DoS
§Example
let mut scanner = SecurityScanner::new(Default::default())?;
scanner.set_plugin_manager(plugin_manager);
Source

pub fn new(config: ScannerConfig) -> Result<Self, ScanError>

Create a new security scanner with the given configuration

Initializes all sub-scanners and loads threat patterns based on the provided configuration.

§Arguments
  • config - Scanner configuration controlling detection features and limits
§Configuration Options
  • unicode_detection: Enable/disable Unicode threat detection
  • injection_detection: Enable/disable injection attack detection
  • xss_detection: Enable/disable XSS detection
  • max_scan_depth: Maximum recursion depth for JSON scanning (default: 20)
  • custom_patterns: Optional path to custom threat pattern file
  • enhanced_mode: Enable enhanced detection with advanced correlation
§Returns
  • Ok(SecurityScanner) - Configured scanner ready for threat detection
  • Err(ScanError) - If pattern loading or scanner initialization fails
§Errors
  • ScanError::PatternError - If custom patterns file is invalid
  • ScanError::InvalidInput - If scanner initialization fails
§Security Best Practices
  • Always validate the custom patterns file path if provided
  • Set appropriate max_scan_depth to prevent stack exhaustion
  • Enable all detection types unless you have specific requirements
§Example
use kindly_guard_server::config::ScannerConfig;
use kindly_guard_server::scanner::SecurityScanner;

// Basic configuration with all protections enabled
let config = ScannerConfig {
    unicode_detection: true,
    injection_detection: true,
    xss_detection: Some(true),
    max_scan_depth: 20,
    custom_patterns: None,
    enhanced_mode: Some(false),
    enable_event_buffer: false,
    crypto_detection: true,
    max_content_size: 5_242_880, // 5MB
};

let scanner = SecurityScanner::new(config)?;
§Performance Notes
  • Scanner initialization is relatively expensive due to pattern compilation
  • Reuse scanner instances across multiple scans for better performance
  • Enhanced mode may increase memory usage but improves detection accuracy
Source

pub fn with_processor( config: ScannerConfig, event_processor: Option<Arc<dyn SecurityEventProcessor>>, ) -> Result<Self, ScanError>

Create a new security scanner with an optional event processor

This method allows creation of a scanner with enhanced capabilities through an event processor for advanced threat correlation and pattern analysis.

§Arguments
  • config - Scanner configuration
  • event_processor - Optional processor for enhanced threat analysis
§Enhanced Mode Features

When an event processor is provided and enable_event_buffer is true:

  • Real-time threat correlation across multiple scans
  • Pattern analysis for identifying attack campaigns
  • Performance optimizations through event batching
  • Advanced metrics and analytics
§Implementation Note

This follows the trait-based architecture pattern where enhanced implementations are hidden behind trait abstractions, allowing for both standard and optimized scanning modes without exposing implementation details.

Source

pub fn scan_text(&self, text: &str) -> ScanResult

Scan text for threats

Source

pub fn scan_json(&self, value: &Value) -> ScanResult

Scan JSON value for security threats

Recursively scans a JSON structure for threats in all string values and object keys. This method is essential for securing API endpoints that accept JSON payloads.

§Arguments
  • value - The JSON value to scan (can be any valid JSON type)
§Returns
  • Ok(Vec<Threat>) - List of detected threats with JSON path locations
  • Err(ScanError) - If scanning fails or depth limit is exceeded
§Security Considerations
  • Depth Limiting: Prevents stack exhaustion from deeply nested JSON
  • Key Scanning: Object keys are scanned as they can contain payloads
  • Path Tracking: Each threat includes the JSON path for precise location
  • Type Safety: Only string values are scanned (numbers/bools are safe)
§JSON Path Format

Threats are reported with JSON paths for easy identification:

  • Root: $
  • Object field: $.field or $.parent.child
  • Array element: $[0] or $.array[2]
  • Nested: $.users[0].name
§Error Handling
  • ScanError::MaxDepthExceeded - If nesting exceeds max_scan_depth
  • ScanError::InvalidInput - If JSON serialization fails
  • Plugin errors are logged but don’t fail the scan
§Example
use kindly_guard_server::scanner::{SecurityScanner, Location};
use serde_json::json;

// Scan a JSON API request
let request = json!({
    "user": {
        "name": "admin' OR '1'='1",
        "bio": "Hello\u{202E}World",  // BiDi override
        "tags": ["safe", "<script>alert(1)</script>"]
    }
});

let threats = scanner.scan_json(&request)?;

for threat in threats {
    if let Location::Json { path } = &threat.location {
        eprintln!("Threat at {}: {}", path, threat.description);
        // Outputs:
        // Threat at $.user.name: SQL Injection
        // Threat at $.user.bio: BiDi Text Spoofing
        // Threat at $.user.tags[1]: Cross-Site Scripting
    }
}
§Performance and Security Trade-offs
  • Depth vs Security: Lower max_scan_depth prevents DoS but may miss threats
  • Memory Usage: Large JSON structures consume memory proportional to depth
  • Scan Time: O(n) where n is total number of string values in JSON
  • Recommendation: Set depth limit based on expected legitimate nesting
§Best Practices
  • Validate JSON schema before scanning for structural attacks
  • Consider rate limiting based on JSON size/complexity
  • Log scan results for security monitoring and pattern analysis
  • Implement allowlists for known-safe patterns to reduce false positives
Source

pub fn stats(&self) -> ScannerStats

Get scanner statistics for monitoring and analysis

Returns current statistics from all enabled scanners including:

  • Total number of scans performed
  • Number of threats detected by type
  • Performance metrics when enhanced mode is enabled
§Thread Safety

Statistics are collected using atomic operations and are safe to read while scanning is in progress on other threads.

§Example
let stats = scanner.stats();
println!("Total scans: {}", stats.total_scans);
println!("Unicode threats: {}", stats.unicode_threats_detected);
println!("Injection threats: {}", stats.injection_threats_detected);

Trait Implementations§

Source§

impl SecurityScannerTrait for SecurityScanner

Source§

fn scan_text(&self, text: &str) -> Vec<Threat>

Scan text for threats
Source§

fn scan_json(&self, value: &Value) -> Vec<Threat>

Scan JSON value for threats
Source§

fn scan_with_depth(&self, text: &str, _max_depth: usize) -> Vec<Threat>

Scan with depth limit
Source§

fn get_stats(&self) -> ScannerStats

Get scanner statistics
Source§

fn reset_stats(&self)

Reset scanner statistics

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,