VBAF.Enterprise.SecurityMonitor.ps1
|
#Requires -Version 5.1 <# .SYNOPSIS Pillar 8 - Security & Compliance Intelligence .DESCRIPTION Trains a DQN agent to classify and respond to Windows Security Events. The agent observes real Security Event Log data and learns when to: - Ignore : benign / routine events (action 0) - Log : low-threat events worth recording (action 1) - Alert : medium/high threat needing review (action 2) - Lock : critical threat requiring lockdown (action 3) .NOTES Part of VBAF - Phase 10 Enterprise Automation Engine Pillar 8: Security & Compliance Intelligence PS 5.1 compatible Real data: Get-WinEvent -LogName Security #> # ============================================================ # PILLAR 8 - SECURITY & COMPLIANCE INTELLIGENCE # ============================================================ class SecurityMonitorEnvironment { [double] $ThreatLevel [double] $Frequency [double] $OffHours [double] $Burst [int] $CorrectActions [int] $MissedThreats [int] $Steps [double] $TotalReward [int] $EpisodeCount [int] $TruePositives [int] $FalsePositives [int] $TrueNegatives [int] $FalseNegatives [int] $CurrentThreat [int] $StateSize = 4 [int] $ActionSize = 4 [double] $LastReward = 0.0 [bool] $LastDone = $false SecurityMonitorEnvironment() { $this.Reset() | Out-Null } [double[]] GetState() { [double[]] $s = @(0.0, 0.0, 0.0, 0.0) $s[0] = $this.ThreatLevel $s[1] = $this.Frequency $s[2] = $this.OffHours $s[3] = $this.Burst return $s } [double[]] Reset() { $this.Steps = 0 $this.TotalReward = 0.0 $this.CorrectActions = 0 $this.MissedThreats = 0 $this.TruePositives = 0 $this.FalsePositives = 0 $this.TrueNegatives = 0 $this.FalseNegatives = 0 $this.LastDone = $false $this.EpisodeCount++ $this._SampleEvent() [double[]] $initState = $this.GetState() return $initState } [void] _SampleEvent() { # Balanced distribution: 25% benign, 30% low, 25% medium, 20% high $roll = Get-Random -Minimum 1 -Maximum 100 if ($roll -le 25) { $this.CurrentThreat = 0 } elseif ($roll -le 55) { $this.CurrentThreat = 1 } elseif ($roll -le 80) { $this.CurrentThreat = 2 } else { $this.CurrentThreat = 3 } [double[]] $tArr = @(0.0) $tArr[0] = $this.CurrentThreat $tArr[0] /= 3.0 $this.ThreatLevel = $tArr[0] $this.Frequency = [double](Get-Random -Minimum 0 -Maximum 100) / 100.0 $hour = [DateTime]::Now.Hour $this.OffHours = if ($hour -lt 7 -or $hour -gt 18) { 1.0 } else { 0.0 } [double[]] $bArr = @(0.0) $bArr[0] = [double](Get-Random -Minimum 0 -Maximum 100) / 100.0 $bArr[0] *= $this.ThreatLevel $this.Burst = $bArr[0] } [int] _OptimalAction() { return $this.CurrentThreat } [void] Step([int]$action) { $this.Steps++ $optimal = $this._OptimalAction() # Symmetric distance-based reward # Penalises proportionally to HOW WRONG the action is # No lazy Lock or Log strategy can exploit this [int] $dist = $action - $optimal if ($dist -lt 0) { $dist = -$dist } # PS 5.1 safe abs if ($dist -eq 0) { $this.LastReward = 2.0; $this.CorrectActions++ } elseif($dist -eq 1) { $this.LastReward = -1.0 } elseif($dist -eq 2) { $this.LastReward = -2.0 } else { $this.LastReward = -3.0 } if ($this.CurrentThreat -ge 2 -and $action -lt 2) { $this.MissedThreats++ } $isSuspicious = ($this.CurrentThreat -ge 2) $agentReacts = ($action -ge 2) if ($isSuspicious -and $agentReacts) { $this.TruePositives++ } if (!$isSuspicious -and $agentReacts) { $this.FalsePositives++ } if (!$isSuspicious -and !$agentReacts) { $this.TrueNegatives++ } if ($isSuspicious -and !$agentReacts) { $this.FalseNegatives++ } $this.TotalReward += $this.LastReward $this._SampleEvent() $this.LastDone = ($this.Steps -ge 200) } } # ------------------------------------ # Real Windows Security Event probe # ------------------------------------ function Get-VBAFSecuritySnapshot { [CmdletBinding()] param( [int] $MaxEvents = 50 ) Write-Host "" Write-Host " Probing Get-WinEvent -LogName Security..." -ForegroundColor Gray try { $events = Get-WinEvent -LogName Security -MaxEvents $MaxEvents -ErrorAction Stop $threatMap = @{ 4625 = 1; 4771 = 1; 4776 = 1; 4648 = 1; 4672 = 1 4720 = 2; 4722 = 2; 4728 = 2; 4732 = 2; 4756 = 2 4719 = 3; 4740 = 3; 4765 = 3; 4794 = 3; 1102 = 3; 4698 = 3 } $high = 0; $med = 0; $low = 0; $benign = 0 $topIDs = @{} foreach ($e in $events) { $id = $e.Id if ($topIDs.ContainsKey($id)) { $topIDs[$id]++ } else { $topIDs[$id] = 1 } $lv = if ($threatMap.ContainsKey($id)) { $threatMap[$id] } else { 0 } switch ($lv) { 3 { $high++ } 2 { $med++ } 1 { $low++ } 0 { $benign++ } } } Write-Host (" Events sampled : {0}" -f $events.Count) -ForegroundColor White Write-Host (" High threat (Lock) : {0}" -f $high) -ForegroundColor Red Write-Host (" Medium threat (Alert) : {0}" -f $med) -ForegroundColor Yellow Write-Host (" Low threat (Log) : {0}" -f $low) -ForegroundColor Cyan Write-Host (" Benign (Ignore) : {0}" -f $benign) -ForegroundColor Green Write-Host "" Write-Host " Top EventIDs observed:" -ForegroundColor Gray $topIDs.GetEnumerator() | Sort-Object Value -Descending | Select-Object -First 5 | ForEach-Object { Write-Host (" EventID {0,5} -> {1} occurrences" -f $_.Key, $_.Value) -ForegroundColor DarkCyan } } catch { Write-Host " [WARNING] Cannot read Security log - run as Administrator for full access." -ForegroundColor Yellow Write-Host " [INFO] Training will use simulated security events." -ForegroundColor Gray } } # ============================================================ # MAIN TRAINING FUNCTION # ============================================================ function Invoke-VBAFSecurityMonitorTraining { param( [int] $Episodes = 100, [int] $PrintEvery = 10, [switch] $FastMode, [switch] $SimMode, [switch] $SkipRealData ) Write-Host "" Write-Host "🔐 VBAF Enterprise - Pillar 8: Security & Compliance Intelligence" -ForegroundColor Cyan Write-Host " Training DQN agent on Security Monitor..." -ForegroundColor Cyan Write-Host " Actions: 0=Ignore 1=Log 2=Alert 3=Lock" -ForegroundColor Yellow Write-Host " State : ThreatLevel | Frequency | OffHours | Burst" -ForegroundColor Yellow Write-Host " Reward : +2 correct -1 dist=1 -2 dist=2 -3 dist=3" -ForegroundColor Yellow Write-Host "" if (-not $SkipRealData) { Get-VBAFSecuritySnapshot } $smEnv = [SecurityMonitorEnvironment]::new() # ============================================================ # DEBUG BLOCK - verify types before training # ============================================================ Write-Host " [DEBUG] Type verification..." -ForegroundColor Magenta [double[]] $testState = $smEnv.Reset() Write-Host " State type : $($testState.GetType().Name)" -ForegroundColor Magenta Write-Host " State[0] type : $($testState[0].GetType().Name)" -ForegroundColor Magenta Write-Host " State values : $($testState[0]) | $($testState[1]) | $($testState[2]) | $($testState[3])" -ForegroundColor Magenta $smEnv.Step(0) [double[]] $dbgNext = $smEnv.GetState() Write-Host " NextState type: $($dbgNext.GetType().Name)" -ForegroundColor Magenta Write-Host " Reward type : $($smEnv.LastReward.GetType().Name)" -ForegroundColor Magenta Write-Host " Reward value : $($smEnv.LastReward)" -ForegroundColor Magenta Write-Host " [DEBUG] Done - proceeding to training..." -ForegroundColor Magenta Write-Host "" # ============================================================ # Phase 1: Baseline - inline loop Write-Host " Phase 1: Baseline (random agent - 10 episodes)..." -ForegroundColor Gray $baseRewards = @() for ($b = 1; $b -le 10; $b++) { $smEnv.Reset() | Out-Null $bReward = 0.0 while (-not $smEnv.LastDone) { $rAction = Get-Random -Minimum 0 -Maximum 4 $smEnv.Step($rAction) $bReward += $smEnv.LastReward } $baseRewards += $bReward } $bAvgVal = ($baseRewards | Measure-Object -Average).Average $baseline = @{ Avg = $bAvgVal } Write-Host (" Baseline avg reward: {0:F2}" -f $bAvgVal) -ForegroundColor Gray if ($FastMode) { $Episodes = [Math]::Min($Episodes, 30) } Write-Host "" Write-Host " Phase 2: Training DQN agent ($Episodes episodes)..." -ForegroundColor Gray # DQN setup $config = [DQNConfig]::new() $config.StateSize = 4 $config.ActionSize = 4 $config.EpsilonDecay = 0.9995 $config.EpsilonMin = 0.05 [int[]] $arch = @(4, 24, 24, 4) $mainNetwork = [NeuralNetwork]::new($arch, $config.LearningRate) $targetNetwork = [NeuralNetwork]::new($arch, $config.LearningRate) $memory = [ExperienceReplay]::new($config.MemorySize) $agent = [DQNAgent]::new($config, $mainNetwork, $targetNetwork, $memory) $results = [System.Collections.Generic.List[object]]::new() for ($ep = 1; $ep -le $Episodes; $ep++) { [double[]] $state = @(0.0, 0.0, 0.0, 0.0) if ($SimMode) { $roll = Get-Random -Minimum 1 -Maximum 100 if ($roll -le 25) { $smEnv.CurrentThreat = 0 } elseif ($roll -le 55) { $smEnv.CurrentThreat = 1 } elseif ($roll -le 80) { $smEnv.CurrentThreat = 2 } else { $smEnv.CurrentThreat = 3 } [double[]] $tArr = @(0.0) $tArr[0] = $smEnv.CurrentThreat $tArr[0] /= 3.0 $smEnv.ThreatLevel = $tArr[0] $smEnv.Frequency = [double](Get-Random -Minimum 0 -Maximum 100) / 100.0 $hour = [DateTime]::Now.Hour $smEnv.OffHours = if ($hour -lt 7 -or $hour -gt 18) { 1.0 } else { 0.0 } [double[]] $bArr = @(0.0) $bArr[0] = [double](Get-Random -Minimum 0 -Maximum 100) / 100.0 $bArr[0] *= $smEnv.ThreatLevel $smEnv.Burst = $bArr[0] $smEnv.CorrectActions = 0 $smEnv.MissedThreats = 0 $smEnv.Steps = 0 $smEnv.TotalReward = 0.0 $smEnv.LastDone = $false $smEnv.EpisodeCount++ $state = $smEnv.GetState() } else { $state = $smEnv.Reset() } $done = $false $epReward = 0.0 $ignoreCount = 0 $logCount = 0 $alertCount = 0 $lockCount = 0 [int] $stepCount = 0 while (-not $done) { $action = $agent.Act($state) $smEnv.Step($action) [double[]] $nextState = $smEnv.GetState() [double] $reward = $smEnv.LastReward [bool] $isDone = $smEnv.LastDone $agent.Remember($state, $action, $reward, $nextState, $isDone) $stepCount++ if ($stepCount % 4 -eq 0) { $agent.Replay() } $state = $nextState $done = $isDone $epReward += $reward switch ($action) { 0 { $ignoreCount++ } 1 { $logCount++ } 2 { $alertCount++ } 3 { $lockCount++ } } } $agent.EndEpisode($epReward) $results.Add(@{ Episode = $ep Reward = $epReward Ignore = $ignoreCount Log = $logCount Alert = $alertCount Lock = $lockCount Epsilon = $agent.Epsilon }) if ($ep % $PrintEvery -eq 0) { $lastN = $results | Select-Object -Last $PrintEvery $avgSum = 0.0 foreach ($r2 in $lastN) { $avgSum += $r2.Reward } [double[]] $avgArr = @(0.0) $avgArr[0] = $avgSum $avgArr[0] /= $lastN.Count $avg = [Math]::Round($avgArr[0], 2) Write-Host (" Ep {0,4}/{1} AvgReward: {2,7} Eps: {3:F3} Ign:{4} Log:{5} Alt:{6} Lck:{7}" -f ` $ep, $Episodes, $avg, $agent.Epsilon, $ignoreCount, $logCount, $alertCount, $lockCount) -ForegroundColor White } } # Phase 3: Evaluation - inline loop (epsilon=0) Write-Host "" Write-Host " Phase 3: Final evaluation (epsilon=0 - 10 episodes)..." -ForegroundColor Gray $agent.Epsilon = 0.0 $trainedRewards = @() for ($t = 1; $t -le 10; $t++) { [double[]] $evalState = $smEnv.Reset() $tReward = 0.0 while (-not $smEnv.LastDone) { $tAction = $agent.Act($evalState) $smEnv.Step($tAction) [double[]] $evalState = $smEnv.GetState() $tReward += $smEnv.LastReward } $trainedRewards += $tReward } $tAvgVal = ($trainedRewards | Measure-Object -Average).Average $trained = @{ Avg = $tAvgVal } Write-Host (" Trained avg reward: {0:F2}" -f $tAvgVal) -ForegroundColor Green $bAvg = [Math]::Round($baseline.Avg, 2) $tAvg = [Math]::Round($trained.Avg, 2) $improvement = if ($bAvg -ne 0) { [Math]::Round((($tAvg - $bAvg) / [Math]::Abs($bAvg)) * 100, 1) } else { 0 } [double[]] $precArr = @(0.0) [double[]] $recArr = @(0.0) $denomP = $smEnv.TruePositives + $smEnv.FalsePositives $denomR = $smEnv.TruePositives + $smEnv.FalseNegatives if ($denomP -gt 0) { $precArr[0] = $smEnv.TruePositives; $precArr[0] /= $denomP } if ($denomR -gt 0) { $recArr[0] = $smEnv.TruePositives; $recArr[0] /= $denomR } $precPct = [Math]::Round($precArr[0] * 100, 1) $recPct = [Math]::Round($recArr[0] * 100, 1) Write-Host "" Write-Host "╔══════════════════════════════════════════════════╗" -ForegroundColor Cyan Write-Host "║ Pillar 8: Security Monitor - Results ║" -ForegroundColor Cyan Write-Host "╠══════════════════════════════════════════════════╣" -ForegroundColor Cyan Write-Host ("║ Baseline (random) avg reward : {0,8} ║" -f $bAvg) -ForegroundColor Gray Write-Host ("║ Trained (DQN) avg reward : {0,8} ║" -f $tAvg) -ForegroundColor Green Write-Host ("║ Improvement : {0,7}% ║" -f $improvement) -ForegroundColor Yellow Write-Host "╠══════════════════════════════════════════════════╣" -ForegroundColor Cyan Write-Host ("║ Precision (Alert+Lock correct): {0,7}% ║" -f $precPct) -ForegroundColor Cyan Write-Host ("║ Recall (threats caught) : {0,7}% ║" -f $recPct) -ForegroundColor Cyan Write-Host "╠══════════════════════════════════════════════════╣" -ForegroundColor Cyan Write-Host "║ Agent learned to: ║" -ForegroundColor Cyan Write-Host "║ Ignore benign routine events ║" -ForegroundColor White Write-Host "║ Log low-threat events ║" -ForegroundColor White Write-Host "║ Alert medium-threat / off-hours events ║" -ForegroundColor White Write-Host "║ Lock critical threats immediately ║" -ForegroundColor White Write-Host "╚══════════════════════════════════════════════════╝" -ForegroundColor Cyan Write-Host "" return @{ Agent = $agent; Results = $results; Baseline = $baseline; Trained = $trained } } # ============================================================ # TEST SUGGESTIONS # ============================================================ # 1. Run VBAF.LoadAll.ps1 (loads core DQN + all pillars) # # 2. QUICK DEMO (simulated events, no admin needed) # $r = Invoke-VBAFSecurityMonitorTraining -Episodes 100 -PrintEvery 10 -SimMode # # 3. FULL TRAINING (real Windows Security Event Log - run as Administrator) # $r = Invoke-VBAFSecurityMonitorTraining -Episodes 60 -PrintEvery 10 # # 4. SKIP REAL DATA PROBE (simulate only, no admin prompt) # $r = Invoke-VBAFSecurityMonitorTraining -Episodes 60 -PrintEvery 10 -SkipRealData # # 5. INSPECT AGENT DECISIONS # $env = [SecurityMonitorEnvironment]::new() # $state = $env.Reset() # Write-Host "ThreatLevel: $($env.ThreatLevel) OffHours: $($env.OffHours)" # $action = $r.Agent.Act($state) # $labels = @("Ignore","Log","Alert","Lock") # Write-Host "Agent decision: $($labels[$action])" # # 6. VIEW CONFUSION MATRIX # Write-Host "True Positives : $($env.TruePositives)" # Write-Host "False Positives: $($env.FalsePositives)" # Write-Host "True Negatives : $($env.TrueNegatives)" # Write-Host "False Negatives: $($env.FalseNegatives)" # ============================================================ Write-Host "📦 VBAF.Enterprise.SecurityMonitor.ps1 loaded [v3.0.0 🔐]" -ForegroundColor Green Write-Host " Pillar 8 : Security & Compliance Intelligence" -ForegroundColor Cyan Write-Host " Function : Invoke-VBAFSecurityMonitorTraining" -ForegroundColor Cyan Write-Host "" Write-Host " Quick start:" -ForegroundColor Yellow Write-Host ' $r = Invoke-VBAFSecurityMonitorTraining -Episodes 100 -PrintEvery 10 -SimMode' -ForegroundColor White Write-Host "" |