Private/Console/Write-WatchtowerReport.ps1
|
# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0 # https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/ # AI/LLM use: see AI-USAGE.md for required attribution function Write-WatchtowerReport { [CmdletBinding()] param( [int]$TotalChanges, [int]$CriticalCount, [int]$HighCount, [int]$MediumCount, [int]$LowCount, [PSCustomObject[]]$NewThreats = @(), [PSCustomObject[]]$FlaggedChanges = @(), [string]$DomainName = '', [string]$ScanMode = 'Fast', [PSCustomObject]$ChangeProfile ) # Calculate Guerrilla Score $guerrillaScore = 100.0 $guerrillaScore -= ($CriticalCount * 25) $guerrillaScore -= ($HighCount * 15) $guerrillaScore -= ($MediumCount * 8) $guerrillaScore -= ($LowCount * 3) $guerrillaScore = [Math]::Max(0, [Math]::Min(100, $guerrillaScore)) $scoreInfo = Get-GuerrillaScoreLabel -Score $guerrillaScore $flaggedCount = $CriticalCount + $HighCount + $MediumCount + $LowCount # --- Header --- $headerContent = @('WATCHTOWER REPORT') if ($DomainName) { $headerContent += "Domain: $DomainName | Mode: $ScanMode" } $headerContent += '' $headerContent += "Guerrilla Score: $('{0,3:N0}' -f $guerrillaScore) / 100 $($scoreInfo.Label)" Write-Host '' Write-SpectrePanel -Content $headerContent -BorderColor Dim -ContentColor Parchment -Width 66 Write-Host '' # Stats Write-GuerrillaText " Total changes: " -Color Olive -NoNewline Write-GuerrillaText ('{0,6:N0}' -f $TotalChanges) -Color White Write-GuerrillaText " Flagged: " -Color Olive -NoNewline Write-GuerrillaText ('{0,6:N0}' -f $flaggedCount) -Color White if ($NewThreats) { Write-GuerrillaText " New threats: " -Color Olive -NoNewline Write-GuerrillaText ('{0,6:N0}' -f $NewThreats.Count) -Color $(if ($NewThreats.Count -gt 0) { 'Amber' } else { 'Sage' }) } Write-Host '' # --- Threat breakdown bar chart --- $threatItems = @() if ($CriticalCount -gt 0) { $threatItems += @{ Label = 'CRITICAL'; Value = $CriticalCount; Color = 'DarkRed' } } if ($HighCount -gt 0) { $threatItems += @{ Label = 'HIGH'; Value = $HighCount; Color = 'DeepOrange' } } if ($MediumCount -gt 0) { $threatItems += @{ Label = 'MEDIUM'; Value = $MediumCount; Color = 'Amber' } } if ($LowCount -gt 0) { $threatItems += @{ Label = 'LOW'; Value = $LowCount; Color = 'Gold' } } if ($threatItems.Count -gt 0) { Write-SpectreBarChart -Items $threatItems -Title 'Threat Breakdown' } if ($flaggedCount -eq 0) { Write-Host '' Write-GuerrillaText ' All clear. No suspicious changes detected in Active Directory.' -Color Sage } # --- New threats table --- if ($NewThreats -and $NewThreats.Count -gt 0) { Write-Host '' $newColumns = @( @{ Name = 'Score'; Color = 'Olive'; Alignment = 'Right' } @{ Name = 'Severity'; Color = 'Olive'; Alignment = 'Left' } @{ Name = 'Detection'; Color = 'Olive'; Alignment = 'Left' } ) $newRows = @() $newRowColors = @() foreach ($t in ($NewThreats | Select-Object -First 10)) { $levelColor = switch ($t.Severity) { 'CRITICAL' { 'DarkRed' } 'HIGH' { 'DeepOrange' } 'MEDIUM' { 'Amber' } 'LOW' { 'Gold' } default { 'Dim' } } $newRows += ,@( ('{0:N0}' -f $t.Score), $t.Severity, $t.DetectionName ) $newRowColors += $levelColor } $newTitle = "New Threats: $($NewThreats.Count)" Write-SpectreTable -Title $newTitle -Columns $newColumns -Rows $newRows -RowColors $newRowColors -BorderColor Dim if ($NewThreats.Count -gt 10) { Write-GuerrillaText " ... and $($NewThreats.Count - 10) more new threat(s)" -Color Dim } } # --- All flagged changes table --- if ($FlaggedChanges -and $FlaggedChanges.Count -gt 0) { Write-Host '' $severityOrder = @{ 'CRITICAL' = 0; 'HIGH' = 1; 'MEDIUM' = 2; 'LOW' = 3 } $sorted = $FlaggedChanges | Sort-Object { if ($severityOrder.ContainsKey($_.Severity)) { $severityOrder[$_.Severity] } else { 99 } } $fcColumns = @( @{ Name = 'Score'; Color = 'Olive'; Alignment = 'Right' } @{ Name = 'Severity'; Color = 'Olive'; Alignment = 'Left' } @{ Name = 'Detection'; Color = 'Olive'; Alignment = 'Left' } @{ Name = 'Detail'; Color = 'Olive'; Alignment = 'Left' } ) $fcRows = @() $fcRowColors = @() foreach ($c in $sorted) { $levelColor = switch ($c.Severity) { 'CRITICAL' { 'DarkRed' } 'HIGH' { 'DeepOrange' } 'MEDIUM' { 'Amber' } 'LOW' { 'Gold' } default { 'Dim' } } $newTag = if ($c.IsNew) { ' [NEW]' } else { '' } $desc = if ($c.Description) { $d = $c.Description if ($d.Length -gt 40) { $d = $d.Substring(0, 37) + '...' } $d } else { '' } $fcRows += ,@( ('{0:N0}' -f $c.Score), "$($c.Severity)$newTag", $c.DetectionName, $desc ) $fcRowColors += $levelColor } Write-SpectreTable -Title 'All Flagged Changes' -Columns $fcColumns -Rows $fcRows -RowColors $fcRowColors -BorderColor Dim } # --- Change category tree --- if ($ChangeProfile) { $children = @() if ($ChangeProfile.GroupChanges.Count -gt 0) { $children += @{ Label = "Group: $($ChangeProfile.GroupChanges.Count)"; Color = 'Olive' } } if ($ChangeProfile.GPOChanges.Count -gt 0) { $children += @{ Label = "GPO: $($ChangeProfile.GPOChanges.Count)"; Color = 'Olive' } } if ($ChangeProfile.GPOLinkChanges.Count -gt 0) { $children += @{ Label = "Links: $($ChangeProfile.GPOLinkChanges.Count)"; Color = 'Olive' } } if ($ChangeProfile.TrustChanges.Count -gt 0) { $children += @{ Label = "Trust: $($ChangeProfile.TrustChanges.Count)"; Color = 'Amber' } } if ($ChangeProfile.ACLChanges.Count -gt 0) { $children += @{ Label = "ACL: $($ChangeProfile.ACLChanges.Count)"; Color = 'Amber' } } if ($ChangeProfile.AdminSDHolderChanged) { $children += @{ Label = 'AdminSDHolder: changed'; Color = 'DarkRed' } } if ($ChangeProfile.KrbtgtChanged) { $children += @{ Label = 'krbtgt: changed'; Color = 'DarkRed' } } if ($ChangeProfile.CertTemplateChanges.Count -gt 0){ $children += @{ Label = "Cert: $($ChangeProfile.CertTemplateChanges.Count)"; Color = 'DeepOrange' } } if ($ChangeProfile.DelegationChanges.Count -gt 0) { $children += @{ Label = "Delegation: $($ChangeProfile.DelegationChanges.Count)"; Color = 'Amber' } } if ($ChangeProfile.DNSChanges.Count -gt 0) { $children += @{ Label = "DNS: $($ChangeProfile.DNSChanges.Count)"; Color = 'Gold' } } if ($ChangeProfile.SchemaChanges.Count -gt 0) { $children += @{ Label = "Schema: $($ChangeProfile.SchemaChanges.Count)"; Color = 'DarkRed' } } if ($ChangeProfile.NewComputers.Count -gt 0) { $children += @{ Label = "New PCs: $($ChangeProfile.NewComputers.Count)"; Color = 'Gold' } } if ($ChangeProfile.NewServiceAccounts.Count -gt 0){ $children += @{ Label = "New Svc: $($ChangeProfile.NewServiceAccounts.Count)"; Color = 'Amber' } } if ($children.Count -gt 0) { Write-Host '' Write-SpectreTree -RootLabel 'Change Categories' -RootColor Parchment -Children $children -GuideColor Dim } } Write-Host '' Write-GuerrillaText ('=' * 62) -Color Dim } |