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!(
468 SecurityEventType::AuthSuccess.default_severity(),
469 SecurityEventSeverity::Low
470 );
471 assert_eq!(
472 SecurityEventType::AuthFailure.default_severity(),
473 SecurityEventSeverity::Medium
474 );
475 assert_eq!(
476 SecurityEventType::AuthzPrivilegeEscalation.default_severity(),
477 SecurityEventSeverity::High
478 );
479 assert_eq!(
480 SecurityEventType::SecurityVulnerabilityDetected.default_severity(),
481 SecurityEventSeverity::Critical
482 );
483 }
484
485 #[test]
486 fn test_compliance_mappings() {
487 let auth_success = SecurityEventType::AuthSuccess;
488 assert!(auth_success.soc2_cc().contains(&"CC6"));
489 assert!(auth_success.iso27001().contains(&"A.9.2"));
490
491 let config_changed = SecurityEventType::ConfigChanged;
492 assert!(config_changed.soc2_cc().contains(&"CC7"));
493 assert!(config_changed.iso27001().contains(&"A.12.1"));
494 }
495
496 #[test]
497 fn test_security_event_creation() {
498 let event = SecurityEvent::new(SecurityEventType::AuthSuccess, None, None);
499 assert_eq!(event.event_type, "auth.success");
500 assert_eq!(event.severity, SecurityEventSeverity::Low);
501 assert_eq!(event.source.system, "mockforge");
502 }
503
504 #[test]
505 fn test_security_event_builder() {
506 let event = SecurityEvent::new(SecurityEventType::AuthFailure, None, None)
507 .with_actor(EventActor {
508 user_id: Some("user-123".to_string()),
509 username: Some("admin".to_string()),
510 ip_address: Some("192.168.1.100".to_string()),
511 user_agent: Some("Mozilla/5.0".to_string()),
512 })
513 .with_target(EventTarget {
514 resource_type: Some("api".to_string()),
515 resource_id: Some("/api/v1/workspaces".to_string()),
516 method: Some("GET".to_string()),
517 })
518 .with_outcome(EventOutcome {
519 success: false,
520 reason: Some("Invalid credentials".to_string()),
521 })
522 .with_metadata("attempt_count".to_string(), serde_json::json!(3));
523
524 assert_eq!(event.event_type, "auth.failure");
525 assert!(event.actor.is_some());
526 assert!(event.target.is_some());
527 assert!(event.outcome.is_some());
528 assert_eq!(
529 event.metadata.get("attempt_count"),
530 Some(&serde_json::json!(3))
531 );
532 }
533
534 #[test]
535 fn test_security_event_serialization() {
536 let event = SecurityEvent::new(SecurityEventType::AuthSuccess, None, None);
537 let json = event.to_json().unwrap();
538 assert!(json.contains("auth.success"));
539 assert!(json.contains("mockforge"));
540 }
541}