Reporting/New-ExecutiveReport.ps1
|
function New-ExecutiveReport { <# .SYNOPSIS Generates comprehensive executive-level security reports from collected metrics .DESCRIPTION Professional report generation tool that transforms security metrics into executive-ready documentation with: - Executive Summary (1-page overview) - Security Score Dashboard - Risk Assessment Matrix - Compliance Framework Breakdown - Threat Landscape Analysis - Vulnerability Management - Trend Analysis (with historical data) - Detailed Findings with Evidence - Prioritized Remediation Roadmap - Resource Planning & Timeline - Multiple Output Formats (HTML, DOCX, PDF, JSON) .PARAMETER MetricsFile Path to the security metrics JSON file generated by Get-SecurityMetrics If not provided, will attempt to use the most recent metrics from history .PARAMETER OutputPath Directory path where the report will be saved Default: Current directory .PARAMETER OutputFormat Report output format Valid values: HTML, DOCX, PDF, JSON, All Default: HTML .PARAMETER ReportTitle Custom title for the report Default: "Security Assessment Executive Report" .PARAMETER CompanyName Company/Organization name to include in the report Default: Domain name or "Organization" .PARAMETER IncludeTrends Include trend analysis section (requires historical metrics data) .PARAMETER IncludeRecommendations Include detailed recommendations section with implementation guidance .PARAMETER IncludeCompliance Include compliance framework comparison and gap analysis .PARAMETER IncludeExecutiveSummary Generate a separate 1-page executive summary document .PARAMETER IncludeCharts Include visual charts and graphs in the report Requires PowerShell charting capabilities .PARAMETER Confidential Mark the report as confidential and add watermarks .PARAMETER Audience Target audience for the report (affects detail level and language) Valid values: Executive, Technical, Mixed Default: Executive .PARAMETER CustomLogo Path to a custom logo image file to include in the report header Supported formats: PNG, JPG, SVG .EXAMPLE New-ExecutiveReport -MetricsFile "C:\Reports\SecurityMetrics_20251008.json" -OutputPath "C:\Reports" -OutputFormat HTML Generate HTML report from specific metrics file .EXAMPLE New-ExecutiveReport -OutputFormat All -IncludeTrends -IncludeCharts -CompanyName "Acme Corp" Generate comprehensive report in all formats with trends and charts .EXAMPLE New-ExecutiveReport -Audience Executive -IncludeExecutiveSummary -Confidential Generate executive-focused report with separate summary and confidential marking .EXAMPLE $metrics = Get-SecurityMetrics -IncludeModules All New-ExecutiveReport -MetricsFile $metrics -OutputFormat DOCX -IncludeCompliance Generate Word document with compliance analysis from in-memory metrics .OUTPUTS Report file(s) in specified format(s): - SecurityReport_YYYYMMDD_HHMMSS.html - SecurityReport_YYYYMMDD_HHMMSS.docx - SecurityReport_YYYYMMDD_HHMMSS.pdf - SecurityReport_YYYYMMDD_HHMMSS.json - ExecutiveSummary_YYYYMMDD_HHMMSS.docx (if IncludeExecutiveSummary) .NOTES Author: WindowsSecurityAudit Module Version: 1.2.0 ULTIMATE EDITION Requires: PowerShell 5.1 or higher Optional: Microsoft Word (for DOCX output) Optional: wkhtmltopdf (for PDF output) Report Sections: 1. Executive Summary 2. Security Score Dashboard 3. Risk Assessment 4. Compliance Status 5. Threat Analysis 6. Vulnerability Summary 7. Findings Details 8. Remediation Roadmap 9. Appendices #> [CmdletBinding()] param( [Parameter()] [object]$MetricsFile, [Parameter()] [string]$OutputPath = ".", [Parameter()] [ValidateSet('HTML', 'DOCX', 'PDF', 'JSON', 'All')] [string]$OutputFormat = 'HTML', [Parameter()] [string]$ReportTitle = "Security Assessment Executive Report", [Parameter()] [string]$CompanyName, [Parameter()] [switch]$IncludeTrends, [Parameter()] [switch]$IncludeRecommendations = $true, [Parameter()] [switch]$IncludeCompliance = $true, [Parameter()] [switch]$IncludeExecutiveSummary, [Parameter()] [switch]$IncludeCharts, [Parameter()] [switch]$Confidential, [Parameter()] [ValidateSet('Executive', 'Technical', 'Mixed')] [string]$Audience = 'Executive', [Parameter()] [string]$CustomLogo ) begin { Write-Host "================================================================" -ForegroundColor Cyan Write-Host " EXECUTIVE REPORT GENERATION - ULTIMATE EDITION" -ForegroundColor Cyan Write-Host "================================================================" -ForegroundColor Cyan $reportStartTime = Get-Date $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" # Ensure output directory exists if (-not (Test-Path $OutputPath)) { New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null Write-Host "Created output directory: $OutputPath" -ForegroundColor Green } # Determine company name if (-not $CompanyName) { $CompanyName = if ($env:USERDNSDOMAIN) { $env:USERDNSDOMAIN } else { "Organization" } } Write-Host "Company: $CompanyName" -ForegroundColor Gray Write-Host "Output Path: $OutputPath" -ForegroundColor Gray Write-Host "Output Format: $OutputFormat" -ForegroundColor Gray Write-Host "Audience: $Audience" -ForegroundColor Gray # Prepare custom logo as base64 data URI (falls back to text header) $logoDataUri = $null if ($CustomLogo) { try { if (Test-Path $CustomLogo) { $logoItem = Get-Item $CustomLogo $extension = $logoItem.Extension.ToLower() $mimeTypes = @{ '.png' = 'image/png' '.jpg' = 'image/jpeg' '.jpeg' = 'image/jpeg' '.svg' = 'image/svg+xml' } if ($mimeTypes.ContainsKey($extension)) { if ($logoItem.Length -le 2MB) { $logoBytes = [System.IO.File]::ReadAllBytes($logoItem.FullName) $logoDataUri = "data:$($mimeTypes[$extension]);base64,$([Convert]::ToBase64String($logoBytes))" Write-Host "Custom Logo: $($logoItem.Name) ($([math]::Round($logoItem.Length / 1KB, 2)) KB)" -ForegroundColor Gray } else { Write-Warning "Custom logo exceeds 2 MB size limit - using text header" } } else { Write-Warning "Custom logo format '$extension' not supported (use PNG, JPG, or SVG) - using text header" } } else { Write-Warning "Custom logo not found: $CustomLogo - using text header" } } catch { Write-Warning "Could not read custom logo '$CustomLogo' - using text header" $logoDataUri = $null } } } process { try { # ============================================ # HELPER FUNCTION - SAFE PROPERTY ACCESS # ============================================ function Get-SafeProperty { param($Object, $PropertyName, $Default = $null) if ($null -eq $Object) { return $Default } if ($Object -is [System.Collections.IDictionary]) { if ($Object.Contains($PropertyName)) { $value = $Object[$PropertyName] if ($null -eq $value) { return $Default } return $value } return $Default } if ($Object.PSObject.Properties[$PropertyName]) { $value = $Object.$PropertyName if ($null -eq $value) { return $Default } return $value } return $Default } # ============================================ # LOAD METRICS DATA # ============================================ Write-Host "`n[LOADING METRICS DATA]" -ForegroundColor Cyan $metrics = $null # A string is always a file path - this check must come FIRST: # '-is [PSCustomObject]' is unreliable for cmdlet-emitted values # (they can test as PSCustomObject regardless of their real type) if ($MetricsFile -is [string] -and -not [string]::IsNullOrWhiteSpace($MetricsFile)) { if (Test-Path $MetricsFile) { try { $metrics = Get-Content $MetricsFile -Raw -Encoding UTF8 | ConvertFrom-Json Write-Host " Loaded metrics from: $MetricsFile" -ForegroundColor Green } catch { Write-Error "Failed to load metrics file: $_" return } } else { # An explicit path was provided but does not exist - never # silently fall back to unrelated history data Write-Error "Metrics file not found: $MetricsFile" return } } # If MetricsFile is an in-memory metrics object (piped from Get-SecurityMetrics) elseif ($MetricsFile) { $metrics = $MetricsFile Write-Host " Using provided metrics object" -ForegroundColor Green } # Try to load most recent from history else { $historyPath = Join-Path $env:TEMP "SecurityMetricsHistory.json" if (Test-Path $historyPath) { try { $history = Get-Content $historyPath -Raw | ConvertFrom-Json $metrics = @($history) | Select-Object -Last 1 Write-Host " Loaded most recent metrics from history" -ForegroundColor Green } catch { Write-Error "Failed to load metrics from history: $_" return } } else { Write-Error "No metrics data found. Please run Get-SecurityMetrics first or provide -MetricsFile parameter." return } } if (-not $metrics) { Write-Error "No metrics data available" return } Write-Host " Metrics Date: $(Get-SafeProperty $metrics 'Timestamp' 'Unknown')" -ForegroundColor Gray Write-Host " Hostname: $(Get-SafeProperty $metrics 'Hostname' 'Unknown')" -ForegroundColor Gray Write-Host " Security Score: $(Get-SafeProperty $metrics 'OverallSecurityScore' 0)%" -ForegroundColor Gray # ============================================ # PREPARE REPORT DATA STRUCTURES # ============================================ Write-Host "`n[PREPARING REPORT DATA]" -ForegroundColor Cyan # Safe nested object access $findingsObj = Get-SafeProperty $metrics 'Findings' @{} $detectionObj = Get-SafeProperty $metrics 'Detection' @{} $vulnerabilityObj = Get-SafeProperty $metrics 'Vulnerability' @{} $complianceObj = Get-SafeProperty $metrics 'Compliance' @{} $defenderObj = Get-SafeProperty $metrics 'WindowsDefender' @{} $coreObj = Get-SafeProperty $metrics 'Core' @{} $recommendationsObj = Get-SafeProperty $metrics 'Recommendations' @{} $trendsObj = Get-SafeProperty $metrics 'Trends' @{} $adObj = Get-SafeProperty $metrics 'ActiveDirectory' @{} $analysisObj = Get-SafeProperty $metrics 'Analysis' @{} $reportData = @{ # Metadata GeneratedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" ReportTitle = $ReportTitle CompanyName = $CompanyName Confidential = $Confidential Audience = $Audience LogoDataUri = $logoDataUri # Source Metrics Metrics = $metrics # Processed Data ExecutiveSummary = @{ SecurityPosture = "" KeyFindings = @() CriticalActions = @() ComplianceStatus = "" OverallRisk = "" } Dashboard = @{ SecurityScore = [int](Get-SafeProperty $metrics 'OverallSecurityScore' 0) SecurityScoreColor = Get-ScoreColor ([int](Get-SafeProperty $metrics 'OverallSecurityScore' 0)) RiskLevel = Get-SafeProperty $metrics 'RiskLevel' 'Unknown' RiskLevelColor = Get-RiskColor (Get-SafeProperty $metrics 'RiskLevel' 'Unknown') ThreatLevel = Get-SafeProperty $metrics 'ThreatLevel' 'Unknown' ThreatLevelColor = Get-ThreatColor (Get-SafeProperty $metrics 'ThreatLevel' 'Unknown') ComplianceScore = [int](Get-SafeProperty $metrics 'ComplianceScore' 0) ComplianceScoreColor = Get-ScoreColor ([int](Get-SafeProperty $metrics 'ComplianceScore' 0)) HealthStatus = Get-SafeProperty $metrics 'HealthStatus' 'Unknown' } RiskAssessment = @{ CriticalRisks = @() HighRisks = @() MediumRisks = @() LowRisks = @() RiskMatrix = @() } Compliance = @{ Frameworks = @() OverallCompliance = [int](Get-SafeProperty $metrics 'ComplianceScore' 0) ControlsTotal = [int](Get-SafeProperty $complianceObj 'TotalControls' 0) ControlsPassed = [int](Get-SafeProperty $complianceObj 'PassedControls' 0) ControlsFailed = [int](Get-SafeProperty $complianceObj 'FailedControls' 0) GapAnalysis = @() } Threats = @{ ActiveThreats = [int](Get-SafeProperty $detectionObj 'TotalThreats' 0) Categories = @() ThreatTimeline = @() } Vulnerabilities = @{ Total = [int](Get-SafeProperty $vulnerabilityObj 'Total' 0) Critical = [int](Get-SafeProperty $vulnerabilityObj 'Critical' 0) High = [int](Get-SafeProperty $vulnerabilityObj 'High' 0) Medium = [int](Get-SafeProperty $vulnerabilityObj 'Medium' 0) Low = [int](Get-SafeProperty $vulnerabilityObj 'Low' 0) EOLSoftware = [int](Get-SafeProperty $vulnerabilityObj 'EOLSoftware' 0) CertificateIssues = [int](Get-SafeProperty $vulnerabilityObj 'CertificateIssues' 0) } Findings = @{ Total = [int](Get-SafeProperty $findingsObj 'Total' 0) Critical = @() High = @() Medium = @() Low = @() } Recommendations = @{ Critical = @() High = @() Medium = @() Low = @() Total = [int](Get-SafeProperty $recommendationsObj 'Total' 0) } RemediationRoadmap = @{ ImmediateActions = @() ShortTerm = @() MediumTerm = @() LongTerm = @() ResourceEstimate = @{ TimeInvestment = "" SkillsRequired = @() EstimatedCost = "" } } Trends = @{ Available = ($IncludeTrends -and [bool](Get-SafeProperty $trendsObj 'Enabled' $false)) SecurityScoreTrend = "" ThreatTrend = "" ComplianceTrend = "" FindingsTrend = "" } Charts = @{ Enabled = $IncludeCharts SecurityScoreGauge = "" FindingsByCategory = "" ComplianceFrameworks = "" ThreatDistribution = "" TrendChart = "" } } # ============================================ # BUILD EXECUTIVE SUMMARY # ============================================ Write-Host " Building executive summary..." -ForegroundColor Gray # Determine security posture description $healthStatus = Get-SafeProperty $metrics 'HealthStatus' 'Unknown' $reportData.ExecutiveSummary.SecurityPosture = switch ($healthStatus) { 'Excellent' { "The organization's security posture is excellent with strong controls in place and minimal risks identified." } 'Good' { "The organization maintains a good security posture with effective controls, though some improvements are recommended." } 'Fair' { "The organization's security posture is fair. Several vulnerabilities and misconfigurations require attention." } 'Poor' { "The organization's security posture is poor. Immediate action is required to address multiple critical security gaps." } 'Critical' { "The organization's security posture is critical. Urgent executive intervention is needed to address severe security deficiencies." } default { "Security posture assessment completed." } } # Safe property access for all metrics $findingsCritical = [int](Get-SafeProperty $findingsObj 'Critical' 0) $detectionThreats = [int](Get-SafeProperty $detectionObj 'TotalThreats' 0) $vulnEOL = [int](Get-SafeProperty $vulnerabilityObj 'EOLSoftware' 0) $complianceScore = [int](Get-SafeProperty $metrics 'ComplianceScore' 0) $defenderEnabled = [bool](Get-SafeProperty $defenderObj 'ProtectionEnabled' $true) $pendingUpdates = [int](Get-SafeProperty $coreObj 'PendingUpdates' 0) # Key findings if ($findingsCritical -gt 0) { $reportData.ExecutiveSummary.KeyFindings += "$findingsCritical critical security findings requiring immediate attention" } if ($detectionThreats -gt 20) { $reportData.ExecutiveSummary.KeyFindings += "$detectionThreats active threats detected across the environment" } if ($vulnEOL -gt 0) { $reportData.ExecutiveSummary.KeyFindings += "$vulnEOL end-of-life software packages present significant risk" } if ($complianceScore -lt 80) { $reportData.ExecutiveSummary.KeyFindings += "Compliance score of $complianceScore% indicates gaps requiring remediation" } if (-not $defenderEnabled) { $reportData.ExecutiveSummary.KeyFindings += "Windows Defender real-time protection is disabled, leaving systems vulnerable" } if ($pendingUpdates -gt 5) { $reportData.ExecutiveSummary.KeyFindings += "$pendingUpdates critical security updates pending installation" } # Critical actions - safe access $criticalRecs = @(Get-SafeProperty $recommendationsObj 'Critical' @()) $highRecs = @(Get-SafeProperty $recommendationsObj 'High' @()) $allCriticalActions = @($criticalRecs) + @($highRecs) if ($allCriticalActions.Count -gt 0) { $reportData.ExecutiveSummary.CriticalActions = $allCriticalActions | Select-Object -First 5 } # Compliance status $reportData.ExecutiveSummary.ComplianceStatus = if ($complianceScore -ge 90) { "Excellent compliance across all tested frameworks" } elseif ($complianceScore -ge 80) { "Good compliance with minor gaps requiring attention" } elseif ($complianceScore -ge 70) { "Fair compliance with several controls requiring remediation" } else { "Compliance gaps present significant regulatory risk" } # Overall risk $riskLevel = Get-SafeProperty $metrics 'RiskLevel' 'Unknown' $reportData.ExecutiveSummary.OverallRisk = switch ($riskLevel) { 'Critical' { "CRITICAL - Immediate executive action required to prevent security incidents" } 'High' { "HIGH - Urgent remediation needed within 7 days to reduce organizational risk" } 'Medium' { "MEDIUM - Remediation plan should be developed and implemented within 30 days" } 'Low' { "LOW - Continue monitoring and maintain current security controls" } default { "Risk assessment completed" } } # ============================================ # BUILD RISK ASSESSMENT # ============================================ Write-Host " Building risk assessment..." -ForegroundColor Gray # Critical risks if ($findingsCritical -gt 0) { $reportData.RiskAssessment.CriticalRisks += @{ Title = "Critical Security Findings" Count = $findingsCritical Impact = "Severe" Description = "Multiple critical vulnerabilities and misconfigurations that could lead to immediate compromise" AffectedSystems = Get-SafeProperty $metrics 'Hostname' 'Unknown' } } if ($vulnEOL -gt 0) { $reportData.RiskAssessment.CriticalRisks += @{ Title = "End-of-Life Software" Count = $vulnEOL Impact = "Severe" Description = "Unsupported software no longer receiving security updates" AffectedSystems = "$vulnEOL applications" } } if (-not $defenderEnabled) { $reportData.RiskAssessment.CriticalRisks += @{ Title = "Disabled Anti-Malware Protection" Count = 1 Impact = "Severe" Description = "Windows Defender real-time protection is disabled" AffectedSystems = Get-SafeProperty $metrics 'Hostname' 'Unknown' } } # High risks if ($detectionThreats -gt 10) { $reportData.RiskAssessment.HighRisks += @{ Title = "Active Threat Detections" Count = $detectionThreats Impact = "High" Description = "Multiple threat indicators across persistence, processes, and network activity" AffectedSystems = Get-SafeProperty $metrics 'Hostname' 'Unknown' } } if ($pendingUpdates -gt 0) { $reportData.RiskAssessment.HighRisks += @{ Title = "Missing Security Updates" Count = $pendingUpdates Impact = "High" Description = "Critical security updates not installed" AffectedSystems = Get-SafeProperty $metrics 'Hostname' 'Unknown' } } $vulnCritical = [int](Get-SafeProperty $vulnerabilityObj 'Critical' 0) if ($vulnCritical -gt 0) { $reportData.RiskAssessment.HighRisks += @{ Title = "Critical Vulnerabilities" Count = $vulnCritical Impact = "High" Description = "Known critical vulnerabilities in installed software" AffectedSystems = "$vulnCritical vulnerable packages" } } # Medium risks $securityMisconfigs = [int](Get-SafeProperty $analysisObj 'SecurityMisconfigurations' 0) if ($securityMisconfigs -gt 0) { $reportData.RiskAssessment.MediumRisks += @{ Title = "Security Misconfigurations" Count = $securityMisconfigs Impact = "Medium" Description = "Configuration settings that weaken security posture" AffectedSystems = Get-SafeProperty $metrics 'Hostname' 'Unknown' } } $certIssues = [int](Get-SafeProperty $vulnerabilityObj 'CertificateIssues' 0) if ($certIssues -gt 0) { $reportData.RiskAssessment.MediumRisks += @{ Title = "Certificate Issues" Count = $certIssues Impact = "Medium" Description = "Expired or weak certificates requiring replacement" AffectedSystems = "$certIssues certificates" } } # Low risks $staleObjects = [int](Get-SafeProperty $adObj 'StaleObjects' 0) if ($staleObjects -gt 10) { $reportData.RiskAssessment.LowRisks += @{ Title = "Stale Active Directory Objects" Count = $staleObjects Impact = "Low" Description = "Inactive user accounts and computer objects requiring cleanup" AffectedSystems = "Active Directory" } } # Risk Matrix $reportData.RiskAssessment.RiskMatrix = @( @{ Category = "Critical"; Count = $reportData.RiskAssessment.CriticalRisks.Count; Color = "Red" } @{ Category = "High"; Count = $reportData.RiskAssessment.HighRisks.Count; Color = "Orange" } @{ Category = "Medium"; Count = $reportData.RiskAssessment.MediumRisks.Count; Color = "Yellow" } @{ Category = "Low"; Count = $reportData.RiskAssessment.LowRisks.Count; Color = "Green" } ) # ============================================ # BUILD COMPLIANCE SECTION # ============================================ if ($IncludeCompliance) { Write-Host " Building compliance analysis..." -ForegroundColor Gray $frameworksObj = Get-SafeProperty $complianceObj 'Frameworks' @{} # CIS Benchmark $cisObj = Get-SafeProperty $frameworksObj 'CIS' @{} $cisTotalControls = [int](Get-SafeProperty $cisObj 'TotalControls' 0) if ($cisTotalControls -gt 0) { $cisScore = [int](Get-SafeProperty $cisObj 'Score' 0) $reportData.Compliance.Frameworks += @{ Name = "CIS Benchmark Level 1" Score = $cisScore TotalControls = $cisTotalControls PassedControls = [int](Get-SafeProperty $cisObj 'PassedControls' 0) FailedControls = [int](Get-SafeProperty $cisObj 'FailedControls' 0) Status = if ($cisScore -ge 80) { "Pass" } elseif ($cisScore -ge 60) { "Marginal" } else { "Fail" } Color = Get-ScoreColor $cisScore } } # NIST 800-53 $nistObj = Get-SafeProperty $frameworksObj 'NIST' @{} $nistTotalControls = [int](Get-SafeProperty $nistObj 'TotalControls' 0) if ($nistTotalControls -gt 0) { $nistScore = [int](Get-SafeProperty $nistObj 'Score' 0) $reportData.Compliance.Frameworks += @{ Name = "NIST 800-53 Rev 5" Score = $nistScore TotalControls = $nistTotalControls PassedControls = [int](Get-SafeProperty $nistObj 'CompliantControls' 0) FailedControls = [int](Get-SafeProperty $nistObj 'NonCompliantControls' 0) Status = if ($nistScore -ge 80) { "Pass" } elseif ($nistScore -ge 60) { "Marginal" } else { "Fail" } Color = Get-ScoreColor $nistScore } } # PCI-DSS $pciObj = Get-SafeProperty $frameworksObj 'PCIDSS' @{} $pciTotalReqs = [int](Get-SafeProperty $pciObj 'TotalRequirements' 0) if ($pciTotalReqs -gt 0) { $pciScore = [int](Get-SafeProperty $pciObj 'Score' 0) $reportData.Compliance.Frameworks += @{ Name = "PCI-DSS v4.0" Score = $pciScore TotalControls = $pciTotalReqs PassedControls = [int](Get-SafeProperty $pciObj 'InPlace' 0) FailedControls = [int](Get-SafeProperty $pciObj 'NotInPlace' 0) Status = if ($pciScore -ge 80) { "Pass" } elseif ($pciScore -ge 60) { "Marginal" } else { "Fail" } Color = Get-ScoreColor $pciScore } } # Gap Analysis $failedControls = [int](Get-SafeProperty $complianceObj 'FailedControls' 0) if ($failedControls -gt 0) { $reportData.Compliance.GapAnalysis += "Total of $failedControls controls require remediation" $reportData.Compliance.GapAnalysis += "Priority focus on CIS and NIST frameworks for immediate compliance improvement" } } # ============================================ # BUILD THREAT ANALYSIS # ============================================ Write-Host " Building threat analysis..." -ForegroundColor Gray $reportData.Threats.Categories = @( @{ Name = "Persistence Mechanisms"; Count = [int](Get-SafeProperty $detectionObj 'PersistenceMechanisms' 0); Severity = "High" } @{ Name = "Suspicious Processes"; Count = [int](Get-SafeProperty $detectionObj 'SuspiciousProcesses' 0); Severity = "High" } @{ Name = "Lateral Movement"; Count = [int](Get-SafeProperty $detectionObj 'LateralMovement' 0); Severity = "Critical" } @{ Name = "APT Indicators"; Count = [int](Get-SafeProperty $detectionObj 'APTIndicators' 0); Severity = "Critical" } @{ Name = "Living Off Land"; Count = [int](Get-SafeProperty $detectionObj 'LivingOffLand' 0); Severity = "Medium" } @{ Name = "Data Exfiltration"; Count = [int](Get-SafeProperty $detectionObj 'DataExfiltration' 0); Severity = "Critical" } @{ Name = "Network Anomalies"; Count = [int](Get-SafeProperty $detectionObj 'NetworkAnomalies' 0); Severity = "Medium" } ) | Where-Object { $_.Count -gt 0 } # ============================================ # BUILD FINDINGS SECTION # ============================================ Write-Host " Building findings section..." -ForegroundColor Gray # Populate findings from metrics if ($findingsCritical -gt 0) { $reportData.Findings.Critical += @{ Title = "Critical Security Vulnerabilities" Count = $findingsCritical Severity = "Critical" Impact = "System compromise, data breach, regulatory penalties" Evidence = "Vulnerability assessment, threat detection scans" Remediation = "Immediate patching, isolation of affected systems, incident response activation" Timeline = "0-24 hours" } } $aptIndicators = [int](Get-SafeProperty $detectionObj 'APTIndicators' 0) if ($aptIndicators -gt 0) { $reportData.Findings.Critical += @{ Title = "Advanced Persistent Threat Indicators" Count = $aptIndicators Severity = "Critical" Impact = "Potential nation-state or advanced adversary activity" Evidence = "APT indicator matching, behavioral analysis" Remediation = "Forensic investigation, threat hunting, IOC analysis" Timeline = "0-24 hours" } } if ($vulnEOL -gt 0) { $reportData.Findings.Critical += @{ Title = "End-of-Life Software Detected" Count = $vulnEOL Severity = "Critical" Impact = "No security updates available, easy exploit targets" Evidence = "Software inventory scan" Remediation = "Software upgrade or replacement, migration planning" Timeline = "1-7 days" } } # High severity findings if ($detectionThreats -gt 10) { $reportData.Findings.High += @{ Title = "Multiple Active Threats Detected" Count = $detectionThreats Severity = "High" Impact = "Elevated risk of security incidents" Evidence = "Threat detection across multiple vectors" Remediation = "Investigate and remediate each threat, enhance monitoring" Timeline = "1-7 days" } } if ($pendingUpdates -gt 0) { $reportData.Findings.High += @{ Title = "Missing Security Updates" Count = $pendingUpdates Severity = "High" Impact = "Known vulnerabilities remain unpatched" Evidence = "Windows Update assessment" Remediation = "Install all pending security updates during maintenance window" Timeline = "7 days" } } # Medium severity findings if ($securityMisconfigs -gt 0) { $reportData.Findings.Medium += @{ Title = "Security Misconfigurations" Count = $securityMisconfigs Severity = "Medium" Impact = "Weakened security posture, potential attack vectors" Evidence = "Configuration assessment" Remediation = "Apply security baseline, harden configurations" Timeline = "30 days" } } if ($certIssues -gt 0) { $reportData.Findings.Medium += @{ Title = "Certificate Health Issues" Count = $certIssues Severity = "Medium" Impact = "Expired or weak certificates, trust issues" Evidence = "Certificate store analysis" Remediation = "Renew/replace certificates, implement monitoring" Timeline = "30 days" } } # ============================================ # BUILD RECOMMENDATIONS # ============================================ if ($IncludeRecommendations) { Write-Host " Building recommendations..." -ForegroundColor Gray $reportData.Recommendations.Critical = @($criticalRecs) | ForEach-Object { @{ Text = $_ Priority = "Critical" Timeline = "Immediate (0-24 hours)" Effort = "High" Impact = "Severe risk reduction" } } $reportData.Recommendations.High = @($highRecs) | ForEach-Object { @{ Text = $_ Priority = "High" Timeline = "Urgent (1-7 days)" Effort = "Medium-High" Impact = "Significant risk reduction" } } $mediumRecs = @(Get-SafeProperty $recommendationsObj 'Medium' @()) $reportData.Recommendations.Medium = @($mediumRecs) | ForEach-Object { @{ Text = $_ Priority = "Medium" Timeline = "Short-term (7-30 days)" Effort = "Medium" Impact = "Moderate risk reduction" } } $lowRecs = @(Get-SafeProperty $recommendationsObj 'Low' @()) $reportData.Recommendations.Low = @($lowRecs) | ForEach-Object { @{ Text = $_ Priority = "Low" Timeline = "Long-term (30-90 days)" Effort = "Low-Medium" Impact = "Minor risk reduction" } } } # ============================================ # BUILD REMEDIATION ROADMAP # ============================================ Write-Host " Building remediation roadmap..." -ForegroundColor Gray # Immediate actions (0-7 days) if ($findingsCritical -gt 0) { $reportData.RemediationRoadmap.ImmediateActions += @{ Action = "Address all critical security findings" Owner = "IT Security Team" Timeline = "0-24 hours" Resources = "Security team, system administrators" Dependencies = "Management approval, system access" } } if (-not $defenderEnabled) { $reportData.RemediationRoadmap.ImmediateActions += @{ Action = "Enable Windows Defender real-time protection" Owner = "IT Operations" Timeline = "Immediate" Resources = "System administrator" Dependencies = "System access, GPO if domain-joined" } } if ($vulnEOL -gt 0) { $reportData.RemediationRoadmap.ImmediateActions += @{ Action = "Develop EOL software replacement plan" Owner = "IT Management" Timeline = "1-7 days" Resources = "IT management, procurement, vendors" Dependencies = "Budget approval, vendor selection" } } # Short-term actions (7-30 days) if ($pendingUpdates -gt 0) { $reportData.RemediationRoadmap.ShortTerm += @{ Action = "Install all pending security updates" Owner = "IT Operations" Timeline = "7-14 days" Resources = "System administrators, change management" Dependencies = "Maintenance window, testing environment" } } if ($complianceScore -lt 80) { $reportData.RemediationRoadmap.ShortTerm += @{ Action = "Remediate compliance control gaps" Owner = "Compliance Team" Timeline = "14-30 days" Resources = "Compliance team, IT security, auditors" Dependencies = "Gap analysis, remediation procedures" } } # Medium-term actions (30-90 days) if ($securityMisconfigs -gt 0) { $reportData.RemediationRoadmap.MediumTerm += @{ Action = "Implement security baseline across all systems" Owner = "IT Security" Timeline = "30-60 days" Resources = "Security team, configuration management tools" Dependencies = "Baseline documentation, testing" } } $reportData.RemediationRoadmap.MediumTerm += @{ Action = "Conduct quarterly security assessments" Owner = "IT Security" Timeline = "Ongoing (90-day cycle)" Resources = "Security team, assessment tools" Dependencies = "Executive sponsorship, budget" } # Long-term actions (90+ days) $reportData.RemediationRoadmap.LongTerm += @{ Action = "Implement continuous security monitoring" Owner = "IT Security" Timeline = "90-180 days" Resources = "SIEM platform, security analysts, SOC" Dependencies = "Tool selection, training, integration" } $reportData.RemediationRoadmap.LongTerm += @{ Action = "Develop comprehensive incident response plan" Owner = "IT Management" Timeline = "90-180 days" Resources = "IR team, legal, external consultants" Dependencies = "Executive approval, training, tabletop exercises" } # Resource estimates $totalFindings = [int](Get-SafeProperty $findingsObj 'Total' 0) $reportData.RemediationRoadmap.ResourceEstimate.TimeInvestment = if ($totalFindings -gt 50) { "6-12 months full-time equivalent" } elseif ($totalFindings -gt 20) { "3-6 months full-time equivalent" } elseif ($totalFindings -gt 10) { "1-3 months full-time equivalent" } else { "2-4 weeks part-time" } $reportData.RemediationRoadmap.ResourceEstimate.SkillsRequired = @( "Security Engineering" "System Administration" "Compliance Management" "Incident Response" "Risk Assessment" ) $reportData.RemediationRoadmap.ResourceEstimate.EstimatedCost = if ($riskLevel -eq 'Critical') { '$$$$$ - Significant investment required' } elseif ($riskLevel -eq 'High') { '$$$$ - Substantial investment needed' } elseif ($riskLevel -eq 'Medium') { '$$$ - Moderate investment' } else { '$$ - Limited investment' } # ============================================ # BUILD TREND ANALYSIS # ============================================ if ($IncludeTrends -and $reportData.Trends.Available) { Write-Host " Building trend analysis..." -ForegroundColor Gray $scoreChange = [int](Get-SafeProperty $trendsObj 'SecurityScoreChange' 0) $scoreTrend = Get-SafeProperty $trendsObj 'SecurityScoreTrend' 'Stable' $reportData.Trends.SecurityScoreTrend = "$scoreTrend ($(if ($scoreChange -gt 0) {'+' })$scoreChange points)" $threatChange = [int](Get-SafeProperty $trendsObj 'ThreatCountChange' 0) $threatTrend = Get-SafeProperty $trendsObj 'ThreatTrend' 'Stable' $reportData.Trends.ThreatTrend = "$threatTrend ($(if ($threatChange -gt 0) {'+' })$threatChange threats)" $compChange = [int](Get-SafeProperty $trendsObj 'ComplianceScoreChange' 0) $compTrend = Get-SafeProperty $trendsObj 'ComplianceTrend' 'Stable' $reportData.Trends.ComplianceTrend = "$compTrend ($(if ($compChange -gt 0) {'+' })$compChange%)" $findingsChange = [int](Get-SafeProperty $trendsObj 'FindingsChange' 0) $findingsTrend = Get-SafeProperty $trendsObj 'FindingsTrend' 'Stable' $reportData.Trends.FindingsTrend = "$findingsTrend ($(if ($findingsChange -gt 0) {'+' })$findingsChange findings)" } # ============================================ # GENERATE REPORT IN REQUESTED FORMAT(S) # ============================================ Write-Host "`n[GENERATING REPORT]" -ForegroundColor Cyan $outputFiles = @() if ($OutputFormat -eq 'All' -or $OutputFormat -eq 'HTML') { Write-Host " Generating HTML report..." -ForegroundColor Gray $htmlFile = Join-Path $OutputPath "SecurityReport_$timestamp.html" $htmlContent = Generate-HTMLReport -ReportData $reportData $htmlContent | Out-File $htmlFile -Encoding UTF8 -Force $outputFiles += $htmlFile Write-Host " HTML report saved: $htmlFile" -ForegroundColor Green } if ($OutputFormat -eq 'All' -or $OutputFormat -eq 'JSON') { Write-Host " Generating JSON report..." -ForegroundColor Gray $jsonFile = Join-Path $OutputPath "SecurityReport_$timestamp.json" $reportData | ConvertTo-Json -Depth 10 | Out-File $jsonFile -Encoding UTF8 -Force $outputFiles += $jsonFile Write-Host " JSON report saved: $jsonFile" -ForegroundColor Green } if ($OutputFormat -eq 'All' -or $OutputFormat -eq 'DOCX') { Write-Host " Generating DOCX report..." -ForegroundColor Gray try { $docxFile = Join-Path $OutputPath "SecurityReport_$timestamp.docx" Generate-DOCXReport -ReportData $reportData -OutputFile $docxFile $outputFiles += $docxFile Write-Host " DOCX report saved: $docxFile" -ForegroundColor Green } catch { Write-Warning " Failed to generate DOCX report: $_" Write-Warning " Microsoft Word may not be installed" } } if ($OutputFormat -eq 'All' -or $OutputFormat -eq 'PDF') { Write-Host " Generating PDF report..." -ForegroundColor Gray Write-Warning " PDF generation requires wkhtmltopdf - converting HTML to PDF" try { $pdfFile = Join-Path $OutputPath "SecurityReport_$timestamp.pdf" $htmlFile = Join-Path $OutputPath "SecurityReport_$timestamp.html" # PDF-only runs still need the intermediate HTML - generate it if the # HTML format was not requested if (-not (Test-Path $htmlFile)) { Write-Host " Generating intermediate HTML for PDF conversion..." -ForegroundColor Gray $pdfHtmlContent = Generate-HTMLReport -ReportData $reportData $pdfHtmlContent | Out-File $htmlFile -Encoding UTF8 -Force } # Try to use wkhtmltopdf if available $wkhtmltopdf = Get-Command wkhtmltopdf -ErrorAction SilentlyContinue if ($wkhtmltopdf) { & wkhtmltopdf "$htmlFile" "$pdfFile" if (Test-Path $pdfFile) { $outputFiles += $pdfFile Write-Host " PDF report saved: $pdfFile" -ForegroundColor Green } else { Write-Warning " wkhtmltopdf did not produce a PDF file" } } else { Write-Warning " wkhtmltopdf not found - install from: https://wkhtmltopdf.org/" } } catch { Write-Warning " Failed to generate PDF report: $_" } } # ============================================ # GENERATE EXECUTIVE SUMMARY (if requested) # ============================================ if ($IncludeExecutiveSummary) { Write-Host "`n[GENERATING EXECUTIVE SUMMARY]" -ForegroundColor Cyan try { $summaryFile = Join-Path $OutputPath "ExecutiveSummary_$timestamp.html" $summaryContent = Generate-ExecutiveSummary -ReportData $reportData $summaryContent | Out-File $summaryFile -Encoding UTF8 -Force $outputFiles += $summaryFile Write-Host " Executive summary saved: $summaryFile" -ForegroundColor Green } catch { Write-Warning " Failed to generate executive summary: $_" } } # ============================================ # RETURN SUMMARY # ============================================ $reportDuration = [Math]::Round(((Get-Date) - $reportStartTime).TotalSeconds, 2) Write-Host "`n================================================================" -ForegroundColor Green Write-Host " REPORT GENERATION COMPLETE" -ForegroundColor Green Write-Host "================================================================" -ForegroundColor Green Write-Host " Generation Time: $reportDuration seconds" -ForegroundColor White Write-Host " Files Generated: $($outputFiles.Count)" -ForegroundColor White foreach ($file in $outputFiles) { $fileSize = [Math]::Round((Get-Item $file).Length / 1KB, 2) Write-Host " - $(Split-Path $file -Leaf) ($fileSize KB)" -ForegroundColor Gray } return [PSCustomObject]@{ Success = $true ReportTitle = $ReportTitle CompanyName = $CompanyName GeneratedDate = $reportData.GeneratedDate OutputFiles = $outputFiles MetricsDate = Get-SafeProperty $metrics 'Timestamp' 'Unknown' SecurityScore = $reportData.Dashboard.SecurityScore RiskLevel = $reportData.Dashboard.RiskLevel TotalFindings = $reportData.Findings.Total GenerationTime = $reportDuration } } catch { Write-Error "Report generation failed: $_" Write-Host "`nStack Trace:" -ForegroundColor Red Write-Host $_.ScriptStackTrace -ForegroundColor Gray return [PSCustomObject]@{ Success = $false Error = $_.Exception.Message } } } } # ============================================ # HELPER FUNCTIONS # ============================================ function Get-ScoreColor { param([int]$Score) if ($Score -ge 90) { return "#28a745" } # Green elseif ($Score -ge 80) { return "#5cb85c" } # Light Green elseif ($Score -ge 70) { return "#f0ad4e" } # Orange elseif ($Score -ge 60) { return "#ff8c00" } # Dark Orange else { return "#d9534f" } # Red } function Get-RiskColor { param([string]$RiskLevel) switch ($RiskLevel) { 'Critical' { return "#8b0000" } # Dark Red 'High' { return "#d9534f" } # Red 'Medium' { return "#f0ad4e" } # Orange 'Low' { return "#5cb85c" } # Green default { return "#808080" } # Gray } } function Get-ThreatColor { param([string]$ThreatLevel) switch ($ThreatLevel) { 'Critical' { return "#8b0000" } # Dark Red 'High' { return "#d9534f" } # Red 'Medium' { return "#f0ad4e" } # Orange 'Low' { return "#f0ad4e" } # Orange 'Minimal' { return "#5cb85c" } # Green default { return "#808080" } # Gray } } function Generate-HTMLReport { param($ReportData) # Safe count helper function Get-SafeCount { param($Array) if ($null -eq $Array) { return 0 } if ($Array -is [array]) { return $Array.Count } if ($Array.PSObject.Properties['Count']) { return $Array.Count } return 1 # Single item } # HTML-encode caller-supplied strings before embedding them in markup $safeTitle = [System.Net.WebUtility]::HtmlEncode("$($ReportData.ReportTitle)") $safeCompany = [System.Net.WebUtility]::HtmlEncode("$($ReportData.CompanyName)") $html = @" <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>$safeTitle</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: #333; background: #f4f4f4; } .container { max-width: 1200px; margin: 0 auto; background: white; box-shadow: 0 0 20px rgba(0,0,0,0.1); } .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px; text-align: center; } .header h1 { font-size: 2.5em; margin-bottom: 10px; } .header .subtitle { font-size: 1.2em; opacity: 0.9; } $(if ($ReportData.Confidential) { ".confidential-banner { background: #d9534f; color: white; text-align: center; padding: 10px; font-weight: bold; font-size: 1.2em; }" }) .section { padding: 40px; border-bottom: 1px solid #e0e0e0; } .section h2 { color: #667eea; margin-bottom: 20px; font-size: 2em; border-bottom: 3px solid #667eea; padding-bottom: 10px; } .dashboard { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin: 20px 0; } .dashboard-card { background: white; border-radius: 10px; padding: 25px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-left: 5px solid; transition: transform 0.2s; } .dashboard-card:hover { transform: translateY(-5px); box-shadow: 0 6px 12px rgba(0,0,0,0.15); } .dashboard-card h3 { font-size: 0.9em; color: #666; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px; } .dashboard-card .value { font-size: 3em; font-weight: bold; margin: 10px 0; } .dashboard-card .label { font-size: 1.1em; color: #666; } .risk-matrix { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 20px 0; } .risk-item { padding: 20px; border-radius: 8px; color: white; text-align: center; } .risk-item h4 { font-size: 1.5em; margin-bottom: 10px; } .risk-item .count { font-size: 3em; font-weight: bold; } .finding { background: #f9f9f9; border-left: 5px solid; padding: 20px; margin: 15px 0; border-radius: 5px; } .finding h4 { font-size: 1.3em; margin-bottom: 10px; } .finding .severity { display: inline-block; padding: 5px 15px; border-radius: 20px; color: white; font-weight: bold; margin-right: 10px; } .finding-detail { margin: 10px 0; padding-left: 20px; } .compliance-framework { background: white; border: 2px solid #e0e0e0; border-radius: 10px; padding: 20px; margin: 15px 0; } .compliance-bar { width: 100%; height: 30px; background: #e0e0e0; border-radius: 15px; overflow: hidden; margin: 10px 0; } .compliance-fill { height: 100%; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; transition: width 1s; } .recommendation { background: #fff3cd; border-left: 5px solid #ffc107; padding: 15px; margin: 10px 0; border-radius: 5px; } .recommendation.critical { background: #f8d7da; border-left-color: #d9534f; } .recommendation.high { background: #ffe8cc; border-left-color: #ff8c00; } .roadmap-section { margin: 20px 0; } .roadmap-item { background: white; border: 2px solid #e0e0e0; border-radius: 8px; padding: 15px; margin: 10px 0; } .roadmap-item h5 { color: #667eea; margin-bottom: 10px; } table { width: 100%; border-collapse: collapse; margin: 20px 0; } th, td { padding: 12px; text-align: left; border-bottom: 1px solid #e0e0e0; } th { background: #667eea; color: white; font-weight: bold; } tr:hover { background: #f5f5f5; } .footer { background: #333; color: white; text-align: center; padding: 30px; } .footer p { margin: 5px 0; } @media print { .section { page-break-inside: avoid; } .dashboard-card { break-inside: avoid; } } </style> </head> <body> <div class="container"> $(if ($ReportData.Confidential) { '<div class="confidential-banner">⚠ CONFIDENTIAL - INTERNAL USE ONLY ⚠</div>' }) <div class="header"> $(if ($ReportData.LogoDataUri) { "<img src='$($ReportData.LogoDataUri)' alt='$safeCompany Logo' style='max-height: 80px; max-width: 300px; margin-bottom: 15px;' />" }) <h1>$safeTitle</h1> <div class="subtitle">$safeCompany</div> <div class="subtitle">Generated: $($ReportData.GeneratedDate)</div> </div> <!-- EXECUTIVE SUMMARY --> <div class="section"> <h2>📊 Executive Summary</h2> <p style="font-size: 1.2em; margin: 20px 0; line-height: 1.8;"> $($ReportData.ExecutiveSummary.SecurityPosture) </p> $(if ((Get-SafeCount $ReportData.ExecutiveSummary.KeyFindings) -gt 0) { "<h3 style='margin-top: 30px; color: #667eea;'>Key Findings:</h3> <ul style='list-style: none; padding: 0;'> $(foreach ($finding in @($ReportData.ExecutiveSummary.KeyFindings)) { "<li style='padding: 10px 0; border-bottom: 1px solid #e0e0e0;'>🔍 $finding</li>" }) </ul>" }) $(if ((Get-SafeCount $ReportData.ExecutiveSummary.CriticalActions) -gt 0) { "<h3 style='margin-top: 30px; color: #d9534f;'>Critical Actions Required:</h3> <ul style='list-style: none; padding: 0;'> $(foreach ($action in @($ReportData.ExecutiveSummary.CriticalActions)) { "<li style='padding: 10px 0; border-bottom: 1px solid #e0e0e0;'>⚠ $action</li>" }) </ul>" }) <div style="background: #f0f8ff; padding: 20px; border-radius: 10px; margin-top: 30px;"> <h3 style="color: #667eea;">Overall Risk Assessment:</h3> <p style="font-size: 1.1em; margin-top: 10px;"> $($ReportData.ExecutiveSummary.OverallRisk) </p> </div> </div> <!-- SECURITY DASHBOARD --> <div class="section"> <h2>📈 Security Dashboard</h2> <div class="dashboard"> <div class="dashboard-card" style="border-left-color: $($ReportData.Dashboard.SecurityScoreColor);"> <h3>Security Score</h3> <div class="value" style="color: $($ReportData.Dashboard.SecurityScoreColor);"> $($ReportData.Dashboard.SecurityScore)% </div> <div class="label">Overall Security Posture</div> </div> <div class="dashboard-card" style="border-left-color: $($ReportData.Dashboard.RiskLevelColor);"> <h3>Risk Level</h3> <div class="value" style="color: $($ReportData.Dashboard.RiskLevelColor);"> $($ReportData.Dashboard.RiskLevel) </div> <div class="label">Organizational Risk</div> </div> <div class="dashboard-card" style="border-left-color: $($ReportData.Dashboard.ThreatLevelColor);"> <h3>Threat Level</h3> <div class="value" style="color: $($ReportData.Dashboard.ThreatLevelColor);"> $($ReportData.Dashboard.ThreatLevel) </div> <div class="label">Active Threat Exposure</div> </div> <div class="dashboard-card" style="border-left-color: $($ReportData.Dashboard.ComplianceScoreColor);"> <h3>Compliance</h3> <div class="value" style="color: $($ReportData.Dashboard.ComplianceScoreColor);"> $($ReportData.Dashboard.ComplianceScore)% </div> <div class="label">Regulatory Compliance</div> </div> </div> </div> <!-- RISK ASSESSMENT --> <div class="section"> <h2>⚠ Risk Assessment</h2> <div class="risk-matrix"> $(foreach ($risk in @($ReportData.RiskAssessment.RiskMatrix)) { "<div class='risk-item' style='background: $($risk.Color);'> <h4>$($risk.Category)</h4> <div class='count'>$($risk.Count)</div> </div>" }) </div> $(if ((Get-SafeCount $ReportData.RiskAssessment.CriticalRisks) -gt 0) { "<h3 style='color: #d9534f; margin-top: 30px;'>🔴 Critical Risks</h3> $(foreach ($risk in @($ReportData.RiskAssessment.CriticalRisks)) { "<div class='finding' style='border-left-color: #d9534f;'> <h4>$($risk.Title) <span style='color: #999;'>($($risk.Count))</span></h4> <p style='margin: 10px 0;'>$($risk.Description)</p> <div class='finding-detail'> <strong>Impact:</strong> $($risk.Impact)<br> <strong>Affected Systems:</strong> $($risk.AffectedSystems) </div> </div>" })" }) $(if ((Get-SafeCount $ReportData.RiskAssessment.HighRisks) -gt 0) { "<h3 style='color: #ff8c00; margin-top: 30px;'>🟠 High Risks</h3> $(foreach ($risk in @($ReportData.RiskAssessment.HighRisks)) { "<div class='finding' style='border-left-color: #ff8c00;'> <h4>$($risk.Title) <span style='color: #999;'>($($risk.Count))</span></h4> <p style='margin: 10px 0;'>$($risk.Description)</p> <div class='finding-detail'> <strong>Impact:</strong> $($risk.Impact)<br> <strong>Affected Systems:</strong> $($risk.AffectedSystems) </div> </div>" })" }) </div> $(if ($IncludeCompliance -and (Get-SafeCount $ReportData.Compliance.Frameworks) -gt 0) { "<!-- COMPLIANCE STATUS --> <div class='section'> <h2>✅ Compliance Status</h2> <p style='font-size: 1.1em; margin-bottom: 20px;'> Overall Compliance Score: <strong>$($ReportData.Compliance.OverallCompliance)%</strong> </p> $(foreach ($framework in @($ReportData.Compliance.Frameworks)) { "<div class='compliance-framework'> <h3>$($framework.Name)</h3> <div class='compliance-bar'> <div class='compliance-fill' style='width: $($framework.Score)%; background: $($framework.Color);'> $($framework.Score)% </div> </div> <p> <strong>Status:</strong> $($framework.Status) | <strong>Controls:</strong> $($framework.PassedControls)/$($framework.TotalControls) passed </p> </div>" }) $(if ((Get-SafeCount $ReportData.Compliance.GapAnalysis) -gt 0) { "<h3 style='margin-top: 30px;'>Gap Analysis:</h3> <ul style='padding-left: 20px;'> $(foreach ($gap in @($ReportData.Compliance.GapAnalysis)) { "<li style='margin: 10px 0;'>$gap</li>" }) </ul>" }) </div>" }) <!-- THREAT ANALYSIS --> <div class="section"> <h2>🎯 Threat Analysis</h2> <p style='font-size: 1.1em; margin-bottom: 20px;'> Total Active Threats: <strong style='color: #d9534f;'>$($ReportData.Threats.ActiveThreats)</strong> </p> $(if ((Get-SafeCount $ReportData.Threats.Categories) -gt 0) { "<table> <tr> <th>Threat Category</th> <th>Count</th> <th>Severity</th> </tr> $(foreach ($category in @($ReportData.Threats.Categories)) { $severityColor = switch ($category.Severity) { 'Critical' { '#d9534f' } 'High' { '#ff8c00' } 'Medium' { '#f0ad4e' } default { '#5cb85c' } } "<tr> <td>$($category.Name)</td> <td><strong>$($category.Count)</strong></td> <td><span style='color: $severityColor; font-weight: bold;'>$($category.Severity)</span></td> </tr>" }) </table>" } else { "<p style='color: #5cb85c; font-size: 1.2em;'>✓ No active threats detected</p>" }) </div> <!-- VULNERABILITIES --> <div class="section"> <h2>🔓 Vulnerability Summary</h2> <div class="dashboard"> <div class="dashboard-card" style="border-left-color: #d9534f;"> <h3>Critical</h3> <div class="value" style="color: #d9534f;">$($ReportData.Vulnerabilities.Critical)</div> </div> <div class="dashboard-card" style="border-left-color: #ff8c00;"> <h3>High</h3> <div class="value" style="color: #ff8c00;">$($ReportData.Vulnerabilities.High)</div> </div> <div class="dashboard-card" style="border-left-color: #f0ad4e;"> <h3>Medium</h3> <div class="value" style="color: #f0ad4e;">$($ReportData.Vulnerabilities.Medium)</div> </div> <div class="dashboard-card" style="border-left-color: #5cb85c;"> <h3>Low</h3> <div class="value" style="color: #5cb85c;">$($ReportData.Vulnerabilities.Low)</div> </div> </div> <p style="margin-top: 20px;"> <strong>EOL Software:</strong> $($ReportData.Vulnerabilities.EOLSoftware) packages require immediate replacement<br> <strong>Certificate Issues:</strong> $($ReportData.Vulnerabilities.CertificateIssues) certificates need attention </p> </div> $(if ($IncludeRecommendations -and ((Get-SafeCount $ReportData.Recommendations.Critical) -gt 0 -or (Get-SafeCount $ReportData.Recommendations.High) -gt 0)) { "<!-- RECOMMENDATIONS --> <div class='section'> <h2>💡 Recommendations</h2> $(if ((Get-SafeCount $ReportData.Recommendations.Critical) -gt 0) { "<h3 style='color: #d9534f;'>Critical Priority</h3> $(foreach ($rec in @($ReportData.Recommendations.Critical)) { "<div class='recommendation critical'> <h4>$($rec.Text)</h4> <p> <strong>Timeline:</strong> $($rec.Timeline) | <strong>Effort:</strong> $($rec.Effort) | <strong>Impact:</strong> $($rec.Impact) </p> </div>" })" }) $(if ((Get-SafeCount $ReportData.Recommendations.High) -gt 0) { "<h3 style='color: #ff8c00; margin-top: 30px;'>High Priority</h3> $(foreach ($rec in @($ReportData.Recommendations.High)) { "<div class='recommendation high'> <h4>$($rec.Text)</h4> <p> <strong>Timeline:</strong> $($rec.Timeline) | <strong>Effort:</strong> $($rec.Effort) | <strong>Impact:</strong> $($rec.Impact) </p> </div>" })" }) </div>" }) <!-- REMEDIATION ROADMAP --> <div class="section"> <h2>🗺 Remediation Roadmap</h2> $(if ((Get-SafeCount $ReportData.RemediationRoadmap.ImmediateActions) -gt 0) { "<div class='roadmap-section'> <h3 style='color: #d9534f;'>🚨 Immediate Actions (0-7 days)</h3> $(foreach ($action in @($ReportData.RemediationRoadmap.ImmediateActions)) { "<div class='roadmap-item'> <h5>$($action.Action)</h5> <p> <strong>Owner:</strong> $($action.Owner)<br> <strong>Timeline:</strong> $($action.Timeline)<br> <strong>Resources:</strong> $($action.Resources)<br> <strong>Dependencies:</strong> $($action.Dependencies) </p> </div>" }) </div>" }) $(if ((Get-SafeCount $ReportData.RemediationRoadmap.ShortTerm) -gt 0) { "<div class='roadmap-section'> <h3 style='color: #ff8c00;'>⚡ Short-Term Actions (7-30 days)</h3> $(foreach ($action in @($ReportData.RemediationRoadmap.ShortTerm)) { "<div class='roadmap-item'> <h5>$($action.Action)</h5> <p> <strong>Owner:</strong> $($action.Owner)<br> <strong>Timeline:</strong> $($action.Timeline)<br> <strong>Resources:</strong> $($action.Resources)<br> <strong>Dependencies:</strong> $($action.Dependencies) </p> </div>" }) </div>" }) <div style="background: #f0f8ff; padding: 20px; border-radius: 10px; margin-top: 30px;"> <h3>📊 Resource Estimate</h3> <p> <strong>Time Investment:</strong> $($ReportData.RemediationRoadmap.ResourceEstimate.TimeInvestment)<br> <strong>Estimated Cost:</strong> $($ReportData.RemediationRoadmap.ResourceEstimate.EstimatedCost)<br> <strong>Skills Required:</strong> $($ReportData.RemediationRoadmap.ResourceEstimate.SkillsRequired -join ', ') </p> </div> </div> $(if ($ReportData.Trends.Available) { "<!-- TREND ANALYSIS --> <div class='section'> <h2>📊 Trend Analysis</h2> <table> <tr> <th>Metric</th> <th>Trend</th> </tr> <tr> <td>Security Score</td> <td>$($ReportData.Trends.SecurityScoreTrend)</td> </tr> <tr> <td>Threats</td> <td>$($ReportData.Trends.ThreatTrend)</td> </tr> <tr> <td>Compliance</td> <td>$($ReportData.Trends.ComplianceTrend)</td> </tr> <tr> <td>Findings</td> <td>$($ReportData.Trends.FindingsTrend)</td> </tr> </table> </div>" }) <div class="footer"> <p><strong>$safeCompany - Security Assessment Report</strong></p> <p>Generated: $($ReportData.GeneratedDate)</p> <p>WindowsSecurityAudit Module v1.2.0</p> $(if ($ReportData.Confidential) { '<p style="margin-top: 15px; color: #d9534f; font-weight: bold;">⚠ CONFIDENTIAL - DO NOT DISTRIBUTE ⚠</p>' }) </div> </div> </body> </html> "@ return $html } function Generate-ExecutiveSummary { param($ReportData) # Safe count helper function Get-SafeCount { param($Array) if ($null -eq $Array) { return 0 } if ($Array -is [array]) { return $Array.Count } if ($Array.PSObject.Properties['Count']) { return $Array.Count } return 1 # Single item } # Simplified 1-page executive summary $safeCompany = [System.Net.WebUtility]::HtmlEncode("$($ReportData.CompanyName)") $html = @" <!DOCTYPE html> <html> <head> <title>Executive Summary - $safeCompany</title> <style> body { font-family: 'Segoe UI', sans-serif; padding: 40px; max-width: 800px; margin: 0 auto; } h1 { color: #667eea; border-bottom: 3px solid #667eea; padding-bottom: 10px; } .summary-box { background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0; } .metric { display: inline-block; margin: 10px 20px 10px 0; } .metric-value { font-size: 2em; font-weight: bold; color: #667eea; } .metric-label { color: #666; } .critical-action { background: #f8d7da; border-left: 5px solid #d9534f; padding: 15px; margin: 10px 0; } ul { line-height: 2; } </style> </head> <body> <h1>Executive Summary</h1> <p><strong>$safeCompany</strong> | $($ReportData.GeneratedDate)</p> <div class="summary-box"> <h2>Overall Security Posture</h2> <p>$($ReportData.ExecutiveSummary.SecurityPosture)</p> <div class="metric"> <div class="metric-value">$($ReportData.Dashboard.SecurityScore)%</div> <div class="metric-label">Security Score</div> </div> <div class="metric"> <div class="metric-value">$($ReportData.Dashboard.RiskLevel)</div> <div class="metric-label">Risk Level</div> </div> <div class="metric"> <div class="metric-value">$($ReportData.Dashboard.ComplianceScore)%</div> <div class="metric-label">Compliance</div> </div> </div> $(if ((Get-SafeCount $ReportData.ExecutiveSummary.KeyFindings) -gt 0) { "<h2>Key Findings</h2> <ul> $(foreach ($finding in @($ReportData.ExecutiveSummary.KeyFindings)) { "<li>$finding</li>" }) </ul>" }) $(if ((Get-SafeCount $ReportData.ExecutiveSummary.CriticalActions) -gt 0) { "<h2>Critical Actions Required</h2> $(foreach ($action in (@($ReportData.ExecutiveSummary.CriticalActions) | Select-Object -First 3)) { "<div class='critical-action'>⚠ $action</div>" })" }) <div class="summary-box" style="margin-top: 30px;"> <h2>Risk Assessment</h2> <p>$($ReportData.ExecutiveSummary.OverallRisk)</p> </div> </body> </html> "@ return $html } function Generate-DOCXReport { param( $ReportData, $OutputFile ) # Try to use Microsoft Word COM object try { $word = New-Object -ComObject Word.Application $word.Visible = $false $doc = $word.Documents.Add() # Add title $selection = $word.Selection $selection.Font.Size = 24 $selection.Font.Bold = $true $selection.Font.Color = 6697198 # Purple $selection.TypeText($ReportData.ReportTitle) $selection.TypeParagraph() $selection.Font.Size = 14 $selection.Font.Bold = $false $selection.Font.Color = 0 $selection.TypeText("$($ReportData.CompanyName) | $($ReportData.GeneratedDate)") $selection.TypeParagraph() $selection.TypeParagraph() # Add executive summary $selection.Font.Size = 18 $selection.Font.Bold = $true $selection.TypeText("Executive Summary") $selection.TypeParagraph() $selection.Font.Size = 11 $selection.Font.Bold = $false $selection.TypeText($ReportData.ExecutiveSummary.SecurityPosture) $selection.TypeParagraph() # Save and close $doc.SaveAs([ref]$OutputFile) $doc.Close() $word.Quit() [System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null Remove-Variable word [System.GC]::Collect() [System.GC]::WaitForPendingFinalizers() } catch { throw "Failed to generate DOCX report: $_" } } |