- ml-observability (1.2K lines): alerts, dashboards, metrics modules. Depends on ml-core + common (ModelType). 4 tests passing. - ml-stress-testing (1.3K lines): load_generator, market_simulator, performance_analyzer modules. Depends on ml-core + common + config. 5 tests passing. - ml-security (1.4K lines): anomaly_detector, prediction_validator modules. Depends on ml-core + ml-ensemble (EnsembleDecision, ModelVote, TradingAction). 17 tests passing. Total: 18 sub-crates extracted from ml monolith. Workspace: 0 errors, ml tests 876 + 26 in new sub-crates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
228 lines
6.7 KiB
Rust
228 lines
6.7 KiB
Rust
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
|
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
|
|
#![allow(dead_code)]
|
|
#![allow(missing_docs)]
|
|
#![allow(missing_debug_implementations)]
|
|
#![allow(unused_crate_dependencies)]
|
|
#![allow(clippy::float_arithmetic)]
|
|
#![allow(clippy::non_ascii_literal)]
|
|
#![allow(clippy::str_to_string)]
|
|
#![allow(clippy::partial_pub_fields)]
|
|
#![allow(clippy::multiple_inherent_impl)]
|
|
#![allow(clippy::same_name_method)]
|
|
#![allow(clippy::shadow_reuse)]
|
|
#![allow(clippy::shadow_unrelated)]
|
|
#![allow(clippy::shadow_same)]
|
|
#![allow(clippy::doc_markdown)]
|
|
#![allow(clippy::indexing_slicing)]
|
|
#![allow(clippy::missing_const_for_fn)]
|
|
#![allow(clippy::module_name_repetitions)]
|
|
#![allow(clippy::integer_division)]
|
|
#![allow(clippy::cognitive_complexity)]
|
|
#![allow(clippy::similar_names)]
|
|
#![allow(clippy::clone_on_ref_ptr)]
|
|
#![allow(clippy::too_many_lines)]
|
|
#![allow(clippy::as_conversions)]
|
|
#![allow(clippy::cast_precision_loss)]
|
|
#![allow(clippy::cast_possible_truncation)]
|
|
#![allow(clippy::default_numeric_fallback)]
|
|
#![allow(clippy::arithmetic_side_effects)]
|
|
#![allow(clippy::needless_range_loop)]
|
|
#![allow(clippy::into_iter_on_ref)]
|
|
#![allow(clippy::new_without_default)]
|
|
#![allow(clippy::manual_let_else)]
|
|
#![allow(clippy::unnecessary_wraps)]
|
|
#![allow(clippy::too_many_arguments)]
|
|
#![allow(clippy::must_use_candidate)]
|
|
#![allow(clippy::missing_errors_doc)]
|
|
#![allow(clippy::cast_sign_loss)]
|
|
#![allow(clippy::cast_possible_wrap)]
|
|
#![allow(clippy::cast_lossless)]
|
|
#![allow(clippy::unused_async)]
|
|
#![allow(clippy::match_same_arms)]
|
|
#![allow(clippy::unused_self)]
|
|
#![allow(clippy::map_err_ignore)]
|
|
#![allow(clippy::single_match_else)]
|
|
#![allow(clippy::wildcard_imports)]
|
|
#![allow(clippy::unnecessary_cast)]
|
|
|
|
//! ML Security Module
|
|
//!
|
|
//! Provides comprehensive security features for ML inference:
|
|
//! - Prediction validation and model poisoning detection
|
|
//! - Ensemble anomaly detection
|
|
//! - Security event logging
|
|
//!
|
|
//! # Architecture
|
|
//!
|
|
//! ```text
|
|
//! ┌──────────────────┐ ┌──────────────────┐
|
|
//! │ Prediction │ │ Ensemble │
|
|
//! │ Validator │──────│ Anomaly │
|
|
//! │ │ │ Detector │
|
|
//! └────────┬─────────┘ └────────┬─────────┘
|
|
//! │ │
|
|
//! └─────────┬───────────────┘
|
|
//! │
|
|
//! ▼
|
|
//! ┌──────────────────┐
|
|
//! │ Security Event │
|
|
//! │ Logger │
|
|
//! └──────────────────┘
|
|
//! ```
|
|
|
|
pub mod anomaly_detector;
|
|
pub mod prediction_validator;
|
|
|
|
pub use anomaly_detector::{
|
|
Anomaly, AnomalyDetectorConfig, AnomalyReport, AnomalySeverity, DetectorStatistics,
|
|
EnsembleAnomalyDetector,
|
|
};
|
|
pub use prediction_validator::{
|
|
PredictionStats, PredictionValidator, ValidatedPrediction, ValidationConfig, ValidationFlag,
|
|
};
|
|
|
|
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Security event types for ML system
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum SecurityEventType {
|
|
// Checkpoint security
|
|
CheckpointSignatureFailure,
|
|
CheckpointSignatureMissing,
|
|
CheckpointTamperingDetected,
|
|
|
|
// Prediction validation
|
|
PredictionOutlierDetected,
|
|
PredictionOutOfBounds,
|
|
ExtremeRateExceeded,
|
|
|
|
// Ensemble anomalies
|
|
EnsembleSuddenShift,
|
|
CoordinatedAttackSuspected,
|
|
ModelBehavioralDrift,
|
|
|
|
// System events
|
|
AutomaticRollback,
|
|
ManualIntervention,
|
|
}
|
|
|
|
/// Security event for logging
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SecurityEvent {
|
|
/// Event type
|
|
pub event_type: SecurityEventType,
|
|
|
|
/// Severity level
|
|
pub severity: SecuritySeverity,
|
|
|
|
/// Event timestamp
|
|
pub timestamp: DateTime<Utc>,
|
|
|
|
/// Model ID (if applicable)
|
|
pub model_id: Option<String>,
|
|
|
|
/// Checkpoint ID (if applicable)
|
|
pub checkpoint_id: Option<String>,
|
|
|
|
/// Prediction ID (if applicable)
|
|
pub prediction_id: Option<String>,
|
|
|
|
/// Human-readable description
|
|
pub description: String,
|
|
|
|
/// Additional metadata
|
|
pub metadata: serde_json::Value,
|
|
|
|
/// Action taken in response
|
|
pub action_taken: Option<String>,
|
|
}
|
|
|
|
/// Security severity levels
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
pub enum SecuritySeverity {
|
|
Low,
|
|
Medium,
|
|
High,
|
|
Critical,
|
|
}
|
|
|
|
impl SecurityEvent {
|
|
/// Create a new security event
|
|
pub fn new(
|
|
event_type: SecurityEventType,
|
|
severity: SecuritySeverity,
|
|
description: String,
|
|
) -> Self {
|
|
Self {
|
|
event_type,
|
|
severity,
|
|
timestamp: Utc::now(),
|
|
model_id: None,
|
|
checkpoint_id: None,
|
|
prediction_id: None,
|
|
description,
|
|
metadata: serde_json::Value::Null,
|
|
action_taken: None,
|
|
}
|
|
}
|
|
|
|
/// Set model ID
|
|
pub fn with_model_id(mut self, model_id: String) -> Self {
|
|
self.model_id = Some(model_id);
|
|
self
|
|
}
|
|
|
|
/// Set checkpoint ID
|
|
pub fn with_checkpoint_id(mut self, checkpoint_id: String) -> Self {
|
|
self.checkpoint_id = Some(checkpoint_id);
|
|
self
|
|
}
|
|
|
|
/// Set prediction ID
|
|
pub fn with_prediction_id(mut self, prediction_id: String) -> Self {
|
|
self.prediction_id = Some(prediction_id);
|
|
self
|
|
}
|
|
|
|
/// Set metadata
|
|
pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
|
|
self.metadata = metadata;
|
|
self
|
|
}
|
|
|
|
/// Set action taken
|
|
pub fn with_action(mut self, action: String) -> Self {
|
|
self.action_taken = Some(action);
|
|
self
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_security_event_builder() {
|
|
let event = SecurityEvent::new(
|
|
SecurityEventType::PredictionOutlierDetected,
|
|
SecuritySeverity::Medium,
|
|
"Test outlier detection".to_owned(),
|
|
)
|
|
.with_model_id("DQN".to_owned())
|
|
.with_action("Flagged for review".to_owned());
|
|
|
|
assert_eq!(event.severity, SecuritySeverity::Medium);
|
|
assert_eq!(event.model_id, Some("DQN".to_owned()));
|
|
assert_eq!(event.action_taken, Some("Flagged for review".to_owned()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_severity_ordering() {
|
|
assert!(SecuritySeverity::Critical > SecuritySeverity::High);
|
|
assert!(SecuritySeverity::High > SecuritySeverity::Medium);
|
|
assert!(SecuritySeverity::Medium > SecuritySeverity::Low);
|
|
}
|
|
}
|