Private/Console/Write-WiretapReport.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-WiretapReport {
    <#
    .SYNOPSIS
        Writes the M365 Wiretap sweep console report.
 
    .DESCRIPTION
        Displays a themed console report summarizing M365 continuous monitoring results,
        including threat level, score, indicators, flagged changes, and new threats.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$TenantId,

        [int]$TotalEvents,

        [string]$ThreatLevel = 'Clean',
        [double]$ThreatScore = 0,

        [string[]]$Indicators = @(),
        [PSCustomObject[]]$FlaggedChanges = @(),
        [PSCustomObject[]]$NewThreats = @(),

        [int]$CriticalCount = 0,
        [int]$HighCount = 0,
        [int]$MediumCount = 0,
        [int]$LowCount = 0
    )

    # Calculate Guerrilla Score (inverse of threat)
    $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

    # --- Header ---
    Write-Host ''
    Write-SpectrePanel -Content @(
        'WIRETAP REPORT'
        'M365 Continuous Security Monitoring'
        ''
        "Tenant: $TenantId"
        "Guerrilla Score: $('{0,3:N0}' -f $guerrillaScore) / 100 $($scoreInfo.Label)"
    ) -BorderColor Dim -ContentColor Parchment -Width 66
    Write-Host ''

    # Threat level
    $threatColor = switch ($ThreatLevel) {
        'CRITICAL' { 'DarkRed' }
        'HIGH'     { 'DeepOrange' }
        'MEDIUM'   { 'Amber' }
        'LOW'      { 'Gold' }
        default    { 'Sage' }
    }

    Write-GuerrillaText ' Threat Level: ' -Color Dim -NoNewline
    Write-GuerrillaText ('{0,-10}' -f $ThreatLevel) -Color $threatColor -Bold -NoNewline
    Write-GuerrillaText ' Score: ' -Color Dim -NoNewline
    Write-GuerrillaText ('{0:N0}' -f $ThreatScore) -Color $threatColor
    Write-Host ''

    # Summary stats
    Write-GuerrillaText ' Events analyzed: ' -Color Olive -NoNewline
    Write-GuerrillaText ('{0,6:N0}' -f $TotalEvents) -Color White
    Write-GuerrillaText ' Flagged changes: ' -Color Olive -NoNewline
    Write-GuerrillaText ('{0,6:N0}' -f $FlaggedChanges.Count) -Color White
    Write-GuerrillaText ' New threats: ' -Color Olive -NoNewline
    Write-GuerrillaText ('{0,6:N0}' -f $NewThreats.Count) -Color White
    Write-Host ''

    # --- Severity 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 'Severity Breakdown'
    }

    if ($FlaggedChanges.Count -eq 0) {
        Write-Host ''
        Write-GuerrillaText ' All clear. No M365 security changes detected.' -Color Sage
    }

    # Indicators
    if ($Indicators.Count -gt 0) {
        Write-Host ''
        Write-GuerrillaText ' Indicators:' -Color Parchment
        foreach ($ind in $Indicators) {
            $indColor = if ($ind -match '^(AUDIT LOG DISABLED|BULK FILE)') { 'DarkRed' }
                        elseif ($ind -match '^(FORWARDING|TRANSPORT|DEFENDER)') { 'DeepOrange' }
                        elseif ($ind -match '^(EDISCOVERY|DLP|POWER)') { 'Amber' }
                        elseif ($ind -match '^(EXTERNAL|TEAMS)') { 'Gold' }
                        else { 'Olive' }
            Write-GuerrillaText " - $ind" -Color $indColor
        }
    }

    # --- Flagged changes table ---
    if ($FlaggedChanges.Count -gt 0) {
        Write-Host ''

        $sortedChanges = @($FlaggedChanges | Sort-Object {
            $sevOrder = @{ 'Critical' = 0; 'High' = 1; 'Medium' = 2; 'Low' = 3 }
            $sevOrder[$_.Severity] ?? 4
        })

        $fcColumns = @(
            @{ Name = 'Severity';  Color = 'Olive';  Alignment = 'Left' }
            @{ Name = 'Type';      Color = 'Olive';  Alignment = 'Left' }
            @{ Name = 'Actor';     Color = 'Olive';  Alignment = 'Left' }
            @{ Name = 'Detail';    Color = 'Olive';  Alignment = 'Left' }
        )

        $fcRows = @()
        $fcRowColors = @()
        foreach ($change in ($sortedChanges | Select-Object -First 15)) {
            $sevColor = switch ($change.Severity) {
                'Critical' { 'DarkRed' }
                'High'     { 'DeepOrange' }
                'Medium'   { 'Amber' }
                'Low'      { 'Gold' }
                default    { 'Dim' }
            }

            $desc = if ($change.Description) {
                $d = $change.Description
                if ($d.Length -gt 35) { $d = $d.Substring(0, 32) + '...' }
                $d
            } else { '' }

            $fcRows += ,@(
                (($change.Severity ?? 'Unknown').ToUpper()),
                ($change.DetectionType ?? ''),
                ($change.Actor ?? 'Unknown'),
                $desc
            )
            $fcRowColors += $sevColor
        }

        Write-SpectreTable -Title 'Flagged Changes' -Columns $fcColumns -Rows $fcRows -RowColors $fcRowColors -BorderColor Dim

        if ($FlaggedChanges.Count -gt 15) {
            Write-GuerrillaText " ... and $($FlaggedChanges.Count - 15) more" -Color Dim
        }
    }

    Write-Host ''
    Write-GuerrillaText ('=' * 62) -Color Dim
}