1use chrono::{DateTime, Utc};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum SecurityEventSeverity {
16 Low,
18 Medium,
20 High,
22 Critical,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(rename_all = "snake_case")]
29pub enum SecurityEventType {
30 AuthSuccess,
33 AuthFailure,
35 AuthTokenExpired,
37 AuthTokenRevoked,
39 AuthMfaEnabled,
41 AuthMfaDisabled,
43 AuthPasswordChanged,
45 AuthPasswordReset,
47
48 AuthzAccessGranted,
51 AuthzAccessDenied,
53 AuthzPrivilegeEscalation,
55 AuthzRoleChanged,
57 AuthzPermissionChanged,
59
60 AccessUserCreated,
63 AccessUserDeleted,
65 AccessUserSuspended,
67 AccessUserActivated,
69 AccessApiTokenCreated,
71 AccessApiTokenDeleted,
73 AccessApiTokenRotated,
75
76 ConfigChanged,
79 ConfigSecurityPolicyUpdated,
81 ConfigEncryptionKeyRotated,
83 ConfigTlsCertificateUpdated,
85
86 DataExported,
89 DataDeleted,
91 DataEncrypted,
93 DataDecrypted,
95 DataClassified,
97
98 SecurityVulnerabilityDetected,
101 SecurityThreatDetected,
103 SecurityAnomalyDetected,
105 SecurityRateLimitExceeded,
107 SecuritySuspiciousActivity,
109
110 ComplianceAuditLogAccessed,
113 ComplianceComplianceCheck,
115 CompliancePolicyViolation,
117}
118
119impl SecurityEventType {
120 pub fn as_str(&self) -> &'static str {
122 match self {
123 SecurityEventType::AuthSuccess => "auth.success",
124 SecurityEventType::AuthFailure => "auth.failure",
125 SecurityEventType::AuthTokenExpired => "auth.token_expired",
126 SecurityEventType::AuthTokenRevoked => "auth.token_revoked",
127 SecurityEventType::AuthMfaEnabled => "auth.mfa_enabled",
128 SecurityEventType::AuthMfaDisabled => "auth.mfa_disabled",
129 SecurityEventType::AuthPasswordChanged => "auth.password_changed",
130 SecurityEventType::AuthPasswordReset => "auth.password_reset",
131 SecurityEventType::AuthzAccessGranted => "authz.access_granted",
132 SecurityEventType::AuthzAccessDenied => "authz.access_denied",
133 SecurityEventType::AuthzPrivilegeEscalation => "authz.privilege_escalation",
134 SecurityEventType::AuthzRoleChanged => "authz.role_changed",
135 SecurityEventType::AuthzPermissionChanged => "authz.permission_changed",
136 SecurityEventType::AccessUserCreated => "access.user_created",
137 SecurityEventType::AccessUserDeleted => "access.user_deleted",
138 SecurityEventType::AccessUserSuspended => "access.user_suspended",
139 SecurityEventType::AccessUserActivated => "access.user_activated",
140 SecurityEventType::AccessApiTokenCreated => "access.api_token_created",
141 SecurityEventType::AccessApiTokenDeleted => "access.api_token_deleted",
142 SecurityEventType::AccessApiTokenRotated => "access.api_token_rotated",
143 SecurityEventType::ConfigChanged => "config.changed",
144 SecurityEventType::ConfigSecurityPolicyUpdated => "config.security_policy_updated",
145 SecurityEventType::ConfigEncryptionKeyRotated => "config.encryption_key_rotated",
146 SecurityEventType::ConfigTlsCertificateUpdated => "config.tls_certificate_updated",
147 SecurityEventType::DataExported => "data.exported",
148 SecurityEventType::DataDeleted => "data.deleted",
149 SecurityEventType::DataEncrypted => "data.encrypted",
150 SecurityEventType::DataDecrypted => "data.decrypted",
151 SecurityEventType::DataClassified => "data.classified",
152 SecurityEventType::SecurityVulnerabilityDetected => "security.vulnerability_detected",
153 SecurityEventType::SecurityThreatDetected => "security.threat_detected",
154 SecurityEventType::SecurityAnomalyDetected => "security.anomaly_detected",
155 SecurityEventType::SecurityRateLimitExceeded => "security.rate_limit_exceeded",
156 SecurityEventType::SecuritySuspiciousActivity => "security.suspicious_activity",
157 SecurityEventType::ComplianceAuditLogAccessed => "compliance.audit_log_accessed",
158 SecurityEventType::ComplianceComplianceCheck => "compliance.compliance_check",
159 SecurityEventType::CompliancePolicyViolation => "compliance.policy_violation",
160 }
161 }
162
163 pub fn default_severity(&self) -> SecurityEventSeverity {
165 match self {
166 SecurityEventType::AuthSuccess
167 | SecurityEventType::AuthzAccessGranted
168 | SecurityEventType::AccessUserCreated
169 | SecurityEventType::AccessUserActivated
170 | SecurityEventType::AccessApiTokenCreated
171 | SecurityEventType::AuthMfaEnabled
172 | SecurityEventType::DataEncrypted
173 | SecurityEventType::DataClassified
174 | SecurityEventType::ComplianceComplianceCheck => SecurityEventSeverity::Low,
175
176 SecurityEventType::AuthFailure
177 | SecurityEventType::AuthTokenExpired
178 | SecurityEventType::AuthTokenRevoked
179 | SecurityEventType::AuthMfaDisabled
180 | SecurityEventType::AuthPasswordChanged
181 | SecurityEventType::AuthPasswordReset
182 | SecurityEventType::AuthzAccessDenied
183 | SecurityEventType::AuthzRoleChanged
184 | SecurityEventType::AuthzPermissionChanged
185 | SecurityEventType::AccessUserSuspended
186 | SecurityEventType::AccessApiTokenDeleted
187 | SecurityEventType::ConfigChanged
188 | SecurityEventType::DataExported
189 | SecurityEventType::DataDeleted
190 | SecurityEventType::SecurityRateLimitExceeded
191 | SecurityEventType::ComplianceAuditLogAccessed => SecurityEventSeverity::Medium,
192
193 SecurityEventType::AuthzPrivilegeEscalation
194 | SecurityEventType::AccessUserDeleted
195 | SecurityEventType::AccessApiTokenRotated
196 | SecurityEventType::ConfigSecurityPolicyUpdated
197 | SecurityEventType::ConfigEncryptionKeyRotated
198 | SecurityEventType::ConfigTlsCertificateUpdated
199 | SecurityEventType::DataDecrypted
200 | SecurityEventType::SecurityThreatDetected
201 | SecurityEventType::SecurityAnomalyDetected
202 | SecurityEventType::SecuritySuspiciousActivity
203 | SecurityEventType::CompliancePolicyViolation => SecurityEventSeverity::High,
204
205 SecurityEventType::SecurityVulnerabilityDetected => SecurityEventSeverity::Critical,
206 }
207 }
208
209 pub fn soc2_cc(&self) -> Vec<&'static str> {
211 match self {
212 SecurityEventType::AuthSuccess
213 | SecurityEventType::AuthFailure
214 | SecurityEventType::AuthTokenExpired
215 | SecurityEventType::AuthTokenRevoked
216 | SecurityEventType::AuthMfaEnabled
217 | SecurityEventType::AuthMfaDisabled
218 | SecurityEventType::AuthPasswordChanged
219 | SecurityEventType::AuthPasswordReset
220 | SecurityEventType::AccessUserCreated
221 | SecurityEventType::AccessUserDeleted
222 | SecurityEventType::AccessUserSuspended
223 | SecurityEventType::AccessUserActivated
224 | SecurityEventType::AccessApiTokenCreated
225 | SecurityEventType::AccessApiTokenDeleted
226 | SecurityEventType::AccessApiTokenRotated
227 | SecurityEventType::AuthzPrivilegeEscalation
228 | SecurityEventType::AuthzRoleChanged
229 | SecurityEventType::AuthzPermissionChanged => vec!["CC6"],
230
231 SecurityEventType::AuthzAccessGranted | SecurityEventType::AuthzAccessDenied => {
232 vec!["CC6"]
233 }
234
235 SecurityEventType::ConfigChanged
236 | SecurityEventType::ConfigSecurityPolicyUpdated
237 | SecurityEventType::ConfigEncryptionKeyRotated
238 | SecurityEventType::ConfigTlsCertificateUpdated => vec!["CC7"],
239
240 SecurityEventType::DataExported
241 | SecurityEventType::DataDeleted
242 | SecurityEventType::DataEncrypted
243 | SecurityEventType::DataDecrypted
244 | SecurityEventType::DataClassified
245 | SecurityEventType::SecurityVulnerabilityDetected
246 | SecurityEventType::SecurityThreatDetected
247 | SecurityEventType::SecurityAnomalyDetected
248 | SecurityEventType::SecurityRateLimitExceeded
249 | SecurityEventType::SecuritySuspiciousActivity
250 | SecurityEventType::ComplianceAuditLogAccessed
251 | SecurityEventType::ComplianceComplianceCheck
252 | SecurityEventType::CompliancePolicyViolation => vec!["CC4"],
253 }
254 }
255
256 pub fn iso27001(&self) -> Vec<&'static str> {
258 match self {
259 SecurityEventType::AuthSuccess
260 | SecurityEventType::AuthFailure
261 | SecurityEventType::AuthTokenExpired
262 | SecurityEventType::AuthTokenRevoked
263 | SecurityEventType::AuthMfaEnabled
264 | SecurityEventType::AuthMfaDisabled
265 | SecurityEventType::AuthPasswordChanged
266 | SecurityEventType::AuthPasswordReset
267 | SecurityEventType::AccessUserCreated
268 | SecurityEventType::AccessUserDeleted
269 | SecurityEventType::AccessUserSuspended
270 | SecurityEventType::AccessUserActivated
271 | SecurityEventType::AccessApiTokenCreated
272 | SecurityEventType::AccessApiTokenDeleted
273 | SecurityEventType::AccessApiTokenRotated
274 | SecurityEventType::AuthzPrivilegeEscalation
275 | SecurityEventType::AuthzRoleChanged
276 | SecurityEventType::AuthzPermissionChanged => vec!["A.9.2"],
277
278 SecurityEventType::AuthzAccessGranted | SecurityEventType::AuthzAccessDenied => {
279 vec!["A.9.4"]
280 }
281
282 SecurityEventType::ConfigChanged
283 | SecurityEventType::ConfigSecurityPolicyUpdated
284 | SecurityEventType::ConfigEncryptionKeyRotated
285 | SecurityEventType::ConfigTlsCertificateUpdated => vec!["A.12.1"],
286
287 SecurityEventType::DataExported
288 | SecurityEventType::DataDeleted
289 | SecurityEventType::DataEncrypted
290 | SecurityEventType::DataDecrypted
291 | SecurityEventType::DataClassified
292 | SecurityEventType::SecurityVulnerabilityDetected
293 | SecurityEventType::SecurityThreatDetected
294 | SecurityEventType::SecurityAnomalyDetected
295 | SecurityEventType::SecurityRateLimitExceeded
296 | SecurityEventType::SecuritySuspiciousActivity
297 | SecurityEventType::ComplianceAuditLogAccessed
298 | SecurityEventType::ComplianceComplianceCheck
299 | SecurityEventType::CompliancePolicyViolation => vec!["A.12.4"],
300 }
301 }
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct EventSource {
307 pub system: String,
309 pub component: String,
311 pub version: String,
313}
314
315impl Default for EventSource {
316 fn default() -> Self {
317 Self {
318 system: "mockforge".to_string(),
319 component: "core".to_string(),
320 version: env!("CARGO_PKG_VERSION").to_string(),
321 }
322 }
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
327pub struct EventActor {
328 pub user_id: Option<String>,
330 pub username: Option<String>,
332 pub ip_address: Option<String>,
334 pub user_agent: Option<String>,
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct EventTarget {
341 pub resource_type: Option<String>,
343 pub resource_id: Option<String>,
345 pub method: Option<String>,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct EventOutcome {
352 pub success: bool,
354 pub reason: Option<String>,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize)]
360pub struct EventCompliance {
361 pub soc2_cc: Vec<String>,
363 pub iso27001: Vec<String>,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct SecurityEvent {
370 pub timestamp: DateTime<Utc>,
372 pub event_type: String,
374 pub severity: SecurityEventSeverity,
376 pub source: EventSource,
378 pub actor: Option<EventActor>,
380 pub target: Option<EventTarget>,
382 pub outcome: Option<EventOutcome>,
384 pub metadata: HashMap<String, serde_json::Value>,
386 pub compliance: EventCompliance,
388}
389
390impl SecurityEvent {
391 pub fn new(
393 event_type: SecurityEventType,
394 severity: Option<SecurityEventSeverity>,
395 source: Option<EventSource>,
396 ) -> Self {
397 let default_severity = event_type.default_severity();
398 let severity = severity.unwrap_or(default_severity);
399
400 Self {
401 timestamp: Utc::now(),
402 event_type: event_type.as_str().to_string(),
403 severity,
404 source: source.unwrap_or_default(),
405 actor: None,
406 target: None,
407 outcome: None,
408 metadata: HashMap::new(),
409 compliance: EventCompliance {
410 soc2_cc: event_type.soc2_cc().iter().map(|s| s.to_string()).collect(),
411 iso27001: event_type.iso27001().iter().map(|s| s.to_string()).collect(),
412 },
413 }
414 }
415
416 pub fn with_actor(mut self, actor: EventActor) -> Self {
418 self.actor = Some(actor);
419 self
420 }
421
422 pub fn with_target(mut self, target: EventTarget) -> Self {
424 self.target = Some(target);
425 self
426 }
427
428 pub fn with_outcome(mut self, outcome: EventOutcome) -> Self {
430 self.outcome = Some(outcome);
431 self
432 }
433
434 pub fn with_metadata(mut self, key: String, value: serde_json::Value) -> Self {
436 self.metadata.insert(key, value);
437 self
438 }
439
440 pub fn to_json(&self) -> Result<String, serde_json::Error> {
442 serde_json::to_string(self)
443 }
444
445 pub fn to_json_value(&self) -> serde_json::Value {
447 serde_json::to_value(self).unwrap_or_default()
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454
455 #[test]
456 fn test_event_type_strings() {
457 assert_eq!(SecurityEventType::AuthSuccess.as_str(), "auth.success");
458 assert_eq!(SecurityEventType::AuthFailure.as_str(), "auth.failure");
459 assert_eq!(
460 SecurityEventType::AuthzPrivilegeEscalation.as_str(),
461 "authz.privilege_escalation"
462 );
463 }
464
465 #[test]
466 fn test_event_severity() {
467 assert_eq!(SecurityEventType::AuthSuccess.default_severity(), SecurityEventSeverity::Low);
468 assert_eq!(
469 SecurityEventType::AuthFailure.default_severity(),
470 SecurityEventSeverity::Medium
471 );
472 assert_eq!(
473 SecurityEventType::AuthzPrivilegeEscalation.default_severity(),
474 SecurityEventSeverity::High
475 );
476 assert_eq!(
477 SecurityEventType::SecurityVulnerabilityDetected.default_severity(),
478 SecurityEventSeverity::Critical
479 );
480 }
481
482 #[test]
483 fn test_compliance_mappings() {
484 let auth_success = SecurityEventType::AuthSuccess;
485 assert!(auth_success.soc2_cc().contains(&"CC6"));
486 assert!(auth_success.iso27001().contains(&"A.9.2"));
487
488 let config_changed = SecurityEventType::ConfigChanged;
489 assert!(config_changed.soc2_cc().contains(&"CC7"));
490 assert!(config_changed.iso27001().contains(&"A.12.1"));
491 }
492
493 #[test]
494 fn test_security_event_creation() {
495 let event = SecurityEvent::new(SecurityEventType::AuthSuccess, None, None);
496 assert_eq!(event.event_type, "auth.success");
497 assert_eq!(event.severity, SecurityEventSeverity::Low);
498 assert_eq!(event.source.system, "mockforge");
499 }
500
501 #[test]
502 fn test_security_event_builder() {
503 let event = SecurityEvent::new(SecurityEventType::AuthFailure, None, None)
504 .with_actor(EventActor {
505 user_id: Some("user-123".to_string()),
506 username: Some("admin".to_string()),
507 ip_address: Some("192.168.1.100".to_string()),
508 user_agent: Some("Mozilla/5.0".to_string()),
509 })
510 .with_target(EventTarget {
511 resource_type: Some("api".to_string()),
512 resource_id: Some("/api/v1/workspaces".to_string()),
513 method: Some("GET".to_string()),
514 })
515 .with_outcome(EventOutcome {
516 success: false,
517 reason: Some("Invalid credentials".to_string()),
518 })
519 .with_metadata("attempt_count".to_string(), serde_json::json!(3));
520
521 assert_eq!(event.event_type, "auth.failure");
522 assert!(event.actor.is_some());
523 assert!(event.target.is_some());
524 assert!(event.outcome.is_some());
525 assert_eq!(event.metadata.get("attempt_count"), Some(&serde_json::json!(3)));
526 }
527
528 #[test]
529 fn test_security_event_serialization() {
530 let event = SecurityEvent::new(SecurityEventType::AuthSuccess, None, None);
531 let json = event.to_json().unwrap();
532 assert!(json.contains("auth.success"));
533 assert!(json.contains("mockforge"));
534 }
535}