kindly_guard_server/
traits.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//! Core trait abstractions for security components
15//! Enables clean separation between standard and enhanced implementations
16
17use crate::scanner::Threat;
18use anyhow::Result;
19use async_trait::async_trait;
20use serde::{Deserialize, Serialize};
21use std::sync::Arc;
22use std::time::Duration;
23
24#[cfg(any(test, feature = "test-utils"))]
25use mockall::{automock, predicate::*};
26
27// Enhanced feature uses local type definitions
28// All types are now defined locally
29
30// Priority levels for events
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum Priority {
33    Normal,
34    Urgent,
35}
36
37// Statistics for an endpoint
38#[derive(Debug, Clone)]
39pub struct EndpointStats {
40    pub success_count: u64,
41    pub failure_count: u64,
42    pub circuit_state: CircuitState,
43    pub available_tokens: u32,
44}
45
46/// Event buffer trait for security event storage and retrieval
47pub trait EventBufferTrait: Send + Sync {
48    /// Enqueue an event in the buffer
49    fn enqueue_event(&self, endpoint_id: u32, data: &[u8], priority: Priority) -> Result<u64>;
50
51    /// Get statistics for an endpoint
52    fn get_endpoint_stats(&self, endpoint_id: u32) -> Result<EndpointStats>;
53}
54
55/// Security event processor trait for handling and correlating events
56#[async_trait]
57#[cfg_attr(any(test, feature = "test-utils"), automock)]
58pub trait SecurityEventProcessor: Send + Sync {
59    /// Process a security event
60    async fn process_event(&self, event: SecurityEvent) -> Result<EventHandle>;
61
62    /// Get processor statistics
63    fn get_stats(&self) -> ProcessorStats;
64
65    /// Check if an endpoint is under monitoring
66    fn is_monitored(&self, endpoint: &str) -> bool;
67
68    /// Get correlation insights for a client
69    async fn get_insights(&self, client_id: &str) -> Result<SecurityInsights>;
70
71    /// Perform cleanup of old events
72    async fn cleanup(&self) -> Result<()>;
73}
74
75/// Enhanced scanner trait for advanced threat detection
76#[cfg_attr(any(test, feature = "test-utils"), automock)]
77pub trait EnhancedScanner: Send + Sync {
78    /// Scan with enhanced capabilities
79    fn enhanced_scan(&self, data: &[u8]) -> Result<Vec<Threat>>;
80
81    /// Get scanner performance metrics
82    fn get_metrics(&self) -> ScannerMetrics;
83
84    /// Preload patterns for optimization
85    fn preload_patterns(&self, patterns: &[String]) -> Result<()>;
86}
87
88/// Correlation engine trait for pattern detection
89#[async_trait]
90#[cfg_attr(any(test, feature = "test-utils"), automock)]
91pub trait CorrelationEngine: Send + Sync {
92    /// Correlate events to detect patterns
93    async fn correlate(&self, events: &[SecurityEvent]) -> Result<Vec<ThreatPattern>>;
94
95    /// Update correlation rules
96    async fn update_rules(&self, rules: CorrelationRules) -> Result<()>;
97
98    /// Get correlation statistics
99    fn get_correlation_stats(&self) -> CorrelationStats;
100}
101
102/// Rate limiter trait for flexible implementations
103#[async_trait]
104#[cfg_attr(any(test, feature = "test-utils"), automock)]
105pub trait RateLimiter: Send + Sync {
106    /// Check if request is allowed
107    async fn check_rate_limit(&self, key: &RateLimitKey) -> Result<RateLimitDecision>;
108
109    /// Record request for rate limiting
110    async fn record_request(&self, key: &RateLimitKey) -> Result<()>;
111
112    /// Apply penalty for threats
113    async fn apply_penalty(&self, client_id: &str, factor: f32) -> Result<()>;
114
115    /// Get rate limit stats
116    fn get_stats(&self) -> RateLimiterStats;
117}
118
119/// Security event types
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct SecurityEvent {
122    pub event_type: String,
123    pub client_id: String,
124    pub timestamp: u64,
125    pub metadata: serde_json::Value,
126}
127
128/// Event processing handle
129#[derive(Debug, Clone)]
130pub struct EventHandle {
131    pub event_id: u64,
132    pub processed: bool,
133}
134
135/// Processor statistics
136#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct ProcessorStats {
138    pub events_processed: u64,
139    pub events_per_second: f64,
140    pub buffer_utilization: f64,
141    pub correlation_hits: u64,
142}
143
144/// Security insights from correlation
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct SecurityInsights {
147    pub risk_score: f32,
148    pub detected_patterns: Vec<String>,
149    pub recommendations: Vec<String>,
150}
151
152/// Scanner performance metrics
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct ScannerMetrics {
155    pub scans_performed: u64,
156    pub threats_detected: u64,
157    pub avg_scan_time_us: u64,
158    pub pattern_cache_hits: u64,
159}
160
161/// Detected threat pattern
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct ThreatPattern {
164    pub pattern_type: String,
165    pub confidence: f32,
166    pub events: Vec<u64>,
167    pub description: String,
168}
169
170/// Correlation rules configuration
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct CorrelationRules {
173    pub time_window: std::time::Duration,
174    pub min_events: usize,
175    pub patterns: Vec<PatternRule>,
176}
177
178/// Individual pattern rule
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct PatternRule {
181    pub name: String,
182    pub event_types: Vec<String>,
183    pub threshold: u32,
184}
185
186/// Correlation statistics
187#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct CorrelationStats {
189    pub patterns_detected: u64,
190    pub false_positives: u64,
191    pub avg_correlation_time_ms: u64,
192}
193
194/// Rate limit key
195#[derive(Debug, Clone, Hash, Eq, PartialEq)]
196pub struct RateLimitKey {
197    pub client_id: String,
198    pub method: Option<String>,
199}
200
201/// Rate limit decision
202#[derive(Debug, Clone)]
203pub struct RateLimitDecision {
204    pub allowed: bool,
205    pub tokens_remaining: f64,
206    pub reset_after: std::time::Duration,
207}
208
209/// Rate limiter statistics
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct RateLimiterStats {
212    pub requests_allowed: u64,
213    pub requests_denied: u64,
214    pub active_buckets: usize,
215}
216
217/// Factory trait for creating security components
218pub trait SecurityComponentFactory: Send + Sync {
219    /// Create event processor
220    fn create_event_processor(
221        &self,
222        config: &crate::config::Config,
223        storage: Arc<dyn crate::storage::StorageProvider>,
224    ) -> Result<Arc<dyn SecurityEventProcessor>>;
225
226    /// Create enhanced scanner
227    fn create_scanner(&self, config: &crate::config::Config) -> Result<Arc<dyn EnhancedScanner>>;
228
229    /// Create correlation engine
230    fn create_correlation_engine(
231        &self,
232        config: &crate::config::Config,
233        storage: Arc<dyn crate::storage::StorageProvider>,
234    ) -> Result<Arc<dyn CorrelationEngine>>;
235
236    /// Create rate limiter
237    fn create_rate_limiter(
238        &self,
239        config: &crate::config::Config,
240        storage: Arc<dyn crate::storage::StorageProvider>,
241    ) -> Result<Arc<dyn RateLimiter>>;
242
243    /// Create security scanner
244    fn create_security_scanner(
245        &self,
246        config: &crate::config::Config,
247    ) -> Result<Arc<dyn SecurityScannerTrait>>;
248}
249
250/// Circuit breaker trait for failure protection
251#[async_trait]
252// NOTE: automock disabled due to compatibility issues with async_trait
253// #[cfg_attr(any(test, feature = "test-utils"), automock)]
254pub trait CircuitBreakerTrait: Send + Sync {
255    /// Execute a function with circuit protection
256    async fn call<F, T, Fut>(&self, name: &str, f: F) -> Result<T, CircuitBreakerError>
257    where
258        F: FnOnce() -> Fut + Send,
259        Fut: std::future::Future<Output = Result<T>> + Send,
260        T: Send;
261
262    /// Get circuit state
263    fn state(&self, name: &str) -> CircuitState;
264
265    /// Get statistics
266    fn stats(&self, name: &str) -> CircuitStats;
267
268    /// Manual circuit control
269    async fn trip(&self, name: &str, reason: &str);
270    async fn reset(&self, name: &str);
271}
272
273/// Retry strategy trait for resilient operations
274#[async_trait]
275// NOTE: automock disabled due to compatibility issues with async_trait
276// #[cfg_attr(any(test, feature = "test-utils"), automock)]
277pub trait RetryStrategyTrait: Send + Sync {
278    /// Execute with retry logic
279    async fn execute<F, T, Fut>(&self, operation: &str, f: F) -> Result<T>
280    where
281        F: Fn() -> Fut + Send + Sync,
282        Fut: std::future::Future<Output = Result<T>> + Send,
283        T: Send;
284
285    /// Analyze error for retry decision
286    fn should_retry(&self, error: &anyhow::Error, context: &RetryContext) -> RetryDecision;
287
288    /// Get retry statistics
289    fn stats(&self) -> RetryStats;
290}
291
292/// Factory trait for resilience components
293pub trait ResilienceFactory: Send + Sync {
294    /// Create circuit breaker (returns dyn-compatible wrapper)
295    fn create_circuit_breaker(
296        &self,
297        config: &crate::config::Config,
298    ) -> Result<Arc<dyn DynCircuitBreaker>>;
299
300    /// Create retry strategy (returns dyn-compatible wrapper)
301    fn create_retry_strategy(
302        &self,
303        config: &crate::config::Config,
304    ) -> Result<Arc<dyn DynRetryStrategy>>;
305
306    /// Create health checker
307    fn create_health_checker(
308        &self,
309        config: &crate::config::Config,
310    ) -> Result<Arc<dyn HealthCheckTrait>>;
311
312    /// Create recovery strategy
313    fn create_recovery_strategy(
314        &self,
315        config: &crate::config::Config,
316    ) -> Result<Arc<dyn RecoveryStrategyTrait>>;
317
318    /// Create bulkhead (returns dyn-compatible wrapper)
319    fn create_bulkhead(
320        &self,
321        config: &crate::config::Config,
322    ) -> Result<Arc<dyn crate::resilience::DynBulkhead>>;
323}
324
325/// Circuit breaker error types
326#[derive(Debug, thiserror::Error, Clone)]
327pub enum CircuitBreakerError {
328    #[error("Circuit breaker is open")]
329    CircuitOpen,
330
331    #[error("Circuit breaker is throttled")]
332    Throttled,
333
334    #[error("Service call failed: {0}")]
335    ServiceError(String),
336
337    #[error("Timeout after {0:?}")]
338    Timeout(Duration),
339}
340
341/// Circuit breaker states
342#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
343pub enum CircuitState {
344    /// Circuit is closed - normal operation
345    Closed,
346    /// Circuit is throttled - degraded operation
347    Throttled,
348    /// Circuit is half-open - testing recovery
349    HalfOpen,
350    /// Circuit is open - all requests blocked
351    Open,
352}
353
354/// Circuit breaker statistics
355#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct CircuitStats {
357    pub state: CircuitState,
358    pub failure_count: u32,
359    pub success_count: u32,
360    pub total_requests: u64,
361    pub last_failure_time: Option<u64>,
362    pub tokens_available: f64,
363}
364
365/// Retry context for decision making
366#[derive(Debug, Clone)]
367pub struct RetryContext {
368    pub attempts: u32,
369    pub error_category: ErrorCategory,
370    pub total_elapsed: Duration,
371}
372
373/// Error categorization
374#[derive(Debug, Clone, Copy)]
375pub struct ErrorCategory {
376    pub is_retryable: bool,
377    pub error_type: ErrorType,
378}
379
380/// Error types for retry decisions
381#[derive(Debug, Clone, Copy, PartialEq, Eq)]
382pub enum ErrorType {
383    Network,
384    Timeout,
385    RateLimit,
386    Authentication,
387    ServerError,
388    ClientError,
389    Unknown,
390}
391
392/// Retry decision
393#[derive(Debug, Clone)]
394pub struct RetryDecision {
395    pub should_retry: bool,
396    pub delay: Option<Duration>,
397    pub reason: String,
398}
399
400/// Retry statistics
401#[derive(Debug, Clone, Serialize, Deserialize)]
402pub struct RetryStats {
403    pub total_attempts: u64,
404    pub successful_retries: u64,
405    pub failed_retries: u64,
406    pub retry_budget_remaining: u32,
407}
408
409/// Health check trait for monitoring service health
410#[async_trait]
411// NOTE: automock disabled due to compatibility issues with async_trait
412// #[cfg_attr(any(test, feature = "test-utils"), automock)]
413pub trait HealthCheckTrait: Send + Sync {
414    /// Perform health check
415    async fn check(&self) -> Result<HealthStatus>;
416
417    /// Get detailed health report
418    async fn detailed_check(&self) -> Result<HealthReport>;
419
420    /// Register dependency health check
421    fn register_dependency(&self, name: String, checker: Arc<dyn HealthCheckTrait>);
422
423    /// Get health check metadata
424    fn metadata(&self) -> HealthCheckMetadata;
425}
426
427/// Recovery strategy trait for failure recovery
428#[async_trait]
429// NOTE: automock disabled due to compatibility issues with async_trait
430// #[cfg_attr(any(test, feature = "test-utils"), automock)]
431pub trait RecoveryStrategyTrait: Send + Sync {
432    /// Execute recovery strategy with a JSON value result
433    async fn recover(
434        &self,
435        context: &RecoveryContext,
436        operation_name: &str,
437    ) -> Result<serde_json::Value>;
438
439    /// Check if recovery is possible
440    fn can_recover(&self, error: &anyhow::Error) -> bool;
441
442    /// Get recovery statistics
443    fn stats(&self) -> RecoveryStats;
444
445    /// Update recovery state
446    async fn update_state(&self, state: RecoveryState);
447}
448
449/// Health status levels
450#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
451pub enum HealthStatus {
452    /// Service is healthy
453    Healthy,
454    /// Service is degraded but operational
455    Degraded,
456    /// Service is unhealthy
457    Unhealthy,
458}
459
460/// Detailed health report
461#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct HealthReport {
463    pub status: HealthStatus,
464    pub checks: Vec<HealthCheckResult>,
465    pub timestamp: u64,
466    pub latency_ms: u64,
467}
468
469/// Individual health check result
470#[derive(Debug, Clone, Serialize, Deserialize)]
471pub struct HealthCheckResult {
472    pub name: String,
473    pub status: HealthStatus,
474    pub message: Option<String>,
475    pub metadata: serde_json::Value,
476}
477
478/// Health check metadata
479#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct HealthCheckMetadata {
481    pub name: String,
482    pub check_type: HealthCheckType,
483    pub timeout: Duration,
484    pub critical: bool,
485}
486
487/// Metrics provider trait for different implementations
488#[cfg_attr(any(test, feature = "test-utils"), automock)]
489pub trait MetricsProvider: Send + Sync {
490    /// Get or create a counter metric
491    fn counter(&self, name: &str, help: &str) -> Arc<dyn CounterTrait>;
492
493    /// Get or create a gauge metric
494    fn gauge(&self, name: &str, help: &str) -> Arc<dyn GaugeTrait>;
495
496    /// Get or create a histogram metric
497    fn histogram(&self, name: &str, help: &str, buckets: Vec<f64>) -> Arc<dyn HistogramTrait>;
498
499    /// Export metrics in Prometheus format
500    fn export_prometheus(&self) -> String;
501
502    /// Export metrics as JSON
503    fn export_json(&self) -> serde_json::Value;
504
505    /// Get uptime in seconds
506    fn uptime_seconds(&self) -> u64;
507}
508
509/// Counter metric trait
510pub trait CounterTrait: Send + Sync {
511    /// Increment the counter by 1
512    fn inc(&self);
513
514    /// Increment the counter by a specific amount
515    fn inc_by(&self, amount: u64);
516
517    /// Get current value
518    fn value(&self) -> u64;
519}
520
521/// Gauge metric trait
522pub trait GaugeTrait: Send + Sync {
523    /// Set the gauge value
524    fn set(&self, value: i64);
525
526    /// Increment the gauge
527    fn inc(&self);
528
529    /// Decrement the gauge
530    fn dec(&self);
531
532    /// Get current value
533    fn value(&self) -> i64;
534}
535
536/// Histogram metric trait
537pub trait HistogramTrait: Send + Sync {
538    /// Record an observation
539    fn observe(&self, value: f64);
540
541    /// Get histogram statistics
542    fn stats(&self) -> HistogramStats;
543}
544
545/// Histogram statistics
546#[derive(Debug, Clone, Serialize, Deserialize)]
547pub struct HistogramStats {
548    pub count: u64,
549    pub sum: f64,
550    pub average: f64,
551}
552
553/// Types of health checks
554#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
555pub enum HealthCheckType {
556    Liveness,
557    Readiness,
558    Startup,
559    Dependency,
560}
561
562/// Recovery context
563#[derive(Debug, Clone)]
564pub struct RecoveryContext {
565    pub failure_count: u32,
566    pub last_error: String,
567    pub recovery_attempts: u32,
568    pub service_name: String,
569}
570
571/// Recovery state
572#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
573pub enum RecoveryState {
574    /// Normal operation
575    Normal,
576    /// Recovering from failure
577    Recovering,
578    /// Using fallback
579    Fallback,
580    /// Recovery failed
581    Failed,
582}
583
584/// Recovery statistics
585#[derive(Debug, Clone, Serialize, Deserialize)]
586pub struct RecoveryStats {
587    pub recoveries_attempted: u64,
588    pub recoveries_succeeded: u64,
589    pub fallbacks_used: u64,
590    pub current_state: RecoveryState,
591}
592
593/// Security scanner trait for threat detection
594#[cfg_attr(any(test, feature = "test-utils"), automock)]
595pub trait SecurityScannerTrait: Send + Sync {
596    /// Scan text for threats
597    fn scan_text(&self, text: &str) -> Vec<crate::scanner::Threat>;
598
599    /// Scan JSON value for threats
600    fn scan_json(&self, value: &serde_json::Value) -> Vec<crate::scanner::Threat>;
601
602    /// Scan with depth limit
603    fn scan_with_depth(&self, text: &str, max_depth: usize) -> Vec<crate::scanner::Threat>;
604
605    /// Get scanner statistics
606    fn get_stats(&self) -> ScannerStats;
607
608    /// Reset scanner statistics
609    fn reset_stats(&self);
610}
611
612/// Scanner statistics
613#[derive(Debug, Clone, Serialize, Deserialize)]
614pub struct ScannerStats {
615    pub texts_scanned: u64,
616    pub threats_found: u64,
617    pub unicode_threats: u64,
618    pub injection_threats: u64,
619    pub pattern_threats: u64,
620    pub avg_scan_time_us: u64,
621}
622
623/// Type-erased circuit breaker for dyn compatibility
624#[async_trait]
625pub trait DynCircuitBreaker: Send + Sync {
626    /// Execute a JSON-RPC call with circuit protection
627    async fn call_json(
628        &self,
629        name: &str,
630        request: serde_json::Value,
631    ) -> Result<serde_json::Value, CircuitBreakerError>;
632
633    /// Get circuit state
634    fn state(&self, name: &str) -> CircuitState;
635
636    /// Get statistics
637    fn stats(&self, name: &str) -> CircuitStats;
638
639    /// Manual circuit control
640    async fn trip(&self, name: &str, reason: &str);
641    async fn reset(&self, name: &str);
642}
643
644/// Type-erased retry strategy for dyn compatibility
645#[async_trait]
646pub trait DynRetryStrategy: Send + Sync {
647    /// Execute a JSON-RPC operation with retry logic
648    async fn execute_json(
649        &self,
650        operation: &str,
651        request: serde_json::Value,
652    ) -> Result<serde_json::Value>;
653
654    /// Analyze error for retry decision
655    fn should_retry(&self, error: &anyhow::Error, context: &RetryContext) -> RetryDecision;
656
657    /// Get retry statistics
658    fn stats(&self) -> RetryStats;
659}
660
661/// Wrapper to adapt `CircuitBreakerTrait` to `DynCircuitBreaker`
662pub struct CircuitBreakerWrapper<T: CircuitBreakerTrait> {
663    inner: T,
664}
665
666impl<T: CircuitBreakerTrait> CircuitBreakerWrapper<T> {
667    pub const fn new(inner: T) -> Self {
668        Self { inner }
669    }
670}
671
672#[async_trait]
673impl<T: CircuitBreakerTrait> DynCircuitBreaker for CircuitBreakerWrapper<T> {
674    async fn call_json(
675        &self,
676        name: &str,
677        request: serde_json::Value,
678    ) -> Result<serde_json::Value, CircuitBreakerError> {
679        self.inner
680            .call(name, || async {
681                // Simulate JSON-RPC processing
682                Ok(serde_json::json!({
683                    "result": "processed",
684                    "request": request
685                }))
686            })
687            .await
688    }
689
690    fn state(&self, name: &str) -> CircuitState {
691        self.inner.state(name)
692    }
693
694    fn stats(&self, name: &str) -> CircuitStats {
695        self.inner.stats(name)
696    }
697
698    async fn trip(&self, name: &str, reason: &str) {
699        self.inner.trip(name, reason).await;
700    }
701
702    async fn reset(&self, name: &str) {
703        self.inner.reset(name).await;
704    }
705}
706
707/// Wrapper to adapt `RetryStrategyTrait` to `DynRetryStrategy`
708pub struct RetryStrategyWrapper<T: RetryStrategyTrait> {
709    inner: T,
710}
711
712impl<T: RetryStrategyTrait> RetryStrategyWrapper<T> {
713    pub const fn new(inner: T) -> Self {
714        Self { inner }
715    }
716}
717
718#[async_trait]
719impl<T: RetryStrategyTrait> DynRetryStrategy for RetryStrategyWrapper<T> {
720    async fn execute_json(
721        &self,
722        operation: &str,
723        request: serde_json::Value,
724    ) -> Result<serde_json::Value> {
725        self.inner
726            .execute(operation, || async {
727                // Simulate JSON-RPC processing
728                Ok(serde_json::json!({
729                    "result": "processed",
730                    "request": request
731                }))
732            })
733            .await
734    }
735
736    fn should_retry(&self, error: &anyhow::Error, context: &RetryContext) -> RetryDecision {
737        self.inner.should_retry(error, context)
738    }
739
740    fn stats(&self) -> RetryStats {
741        self.inner.stats()
742    }
743}