Public/New-LS2Dashboard.ps1

function New-LS2Dashboard {
    <#
    .SYNOPSIS
    Generates an interactive HTML dashboard for Locksmith2 scan results.
    
    .DESCRIPTION
    Creates a comprehensive HTML dashboard with left navigation menu showing:
    - All issues with expanded principals
    - Issues filtered by type (Template, CA, Object)
    - Risky principals analysis
    
    Supports light/dark mode toggle and interactive filtering/sorting.
    Requires PSWriteHTML module (Install-Module PSWriteHTML).
    
    .PARAMETER FilePath
    Path where the HTML dashboard will be saved.
    Default: $env:TEMP\Locksmith2-Dashboard.html
    
    .PARAMETER Show
    Opens the dashboard in default browser after generation.
    
    .PARAMETER ExpandGroups
    Expands group principals into individual member issues.
    
    .PARAMETER Online
    Uses online CDN resources instead of embedding CSS/JS.
    Results in smaller file size but requires internet connection to view.
    
    .INPUTS
    None. This function does not accept pipeline input.
    
    .OUTPUTS
    None. Generates an HTML file at the specified path.
    
    .EXAMPLE
    Invoke-Locksmith2
    New-LS2Dashboard -FilePath C:\Reports\locksmith-dashboard.html -Show
    
    Runs a scan and generates an interactive dashboard.
    
    .EXAMPLE
    New-LS2Dashboard -ExpandGroups -Show
    
    Generates dashboard with group memberships expanded to individual principals.
    
    .EXAMPLE
    New-LS2Dashboard -FilePath C:\Reports\report.html -Online
    
    Generates a smaller dashboard file using online CDN resources.
    
    .NOTES
    Author: Jake Hildreth (@jakehildreth)
    Requires: PSWriteHTML module (https://github.com/EvotecIT/PSWriteHTML)
    Requires: PowerShell 5.1 or later
    
    The dashboard reads from the current IssueStore. Run Invoke-Locksmith2 or
    Find-LS2Vulnerable* functions first to populate the store with scan data.
    
    .LINK
    Invoke-Locksmith2
    
    .LINK
    Find-LS2RiskyPrincipal
    
    .LINK
    Get-FlattenedIssues
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$FilePath = "$env:TEMP\Locksmith2-Dashboard.html",
        
        [Parameter()]
        [switch]$Show,
        
        [Parameter()]
        [switch]$ExpandGroups,
        
        [Parameter()]
        [switch]$Online
    )
    
    #requires -Version 5.1
    
    # Check for PSWriteHTML module
    if (-not (Get-Module -ListAvailable -Name PSWriteHTML)) {
        Write-Error "PSWriteHTML module is required. Install with: Install-Module PSWriteHTML"
        return
    }
    
    Import-Module PSWriteHTML -ErrorAction Stop
    
    # Check if IssueStore is populated
    if (-not $script:IssueStore -or $script:IssueStore.Count -eq 0) {
        Write-Warning "IssueStore is empty. Run Invoke-Locksmith2 or Find-LS2Vulnerable* functions first."
        Write-Warning "Generating empty dashboard..."
    }
    
    # Get all issues from IssueStore
    $allIssues = Get-FlattenedIssues
    
    # Expand groups if requested
    if ($ExpandGroups) {
        Write-Verbose "Expanding group memberships for dashboard..."
        $allIssues = $allIssues | ForEach-Object { Expand-IssueByGroup $_ }
    }
    
    # Filter issues by category
    $templateTechniques = @('ESC1', 'ESC2', 'ESC3c1', 'ESC3c2', 'ESC4a', 'ESC4o', 'ESC9')
    $caTechniques = @('ESC6', 'ESC7a', 'ESC7m', 'ESC11', 'ESC16')
    $objectTechniques = @('ESC5a', 'ESC5o')
    $misconfigurationTechniques = @('ESC1', 'ESC2', 'ESC3c1', 'ESC3c1', 'ESC6', 'ESC9', 'ESC11', 'ESC16')
    $accessTechniques = @('ESC4a', 'ESC5a')
    $ownershipTechniques = @('ESC4o', 'ESC5o')
    
    $templateIssues = $allIssues | Where-Object { $_.Technique -in $templateTechniques }
    $caIssues = $allIssues | Where-Object { $_.Technique -in $caTechniques }
    $objectIssues = $allIssues | Where-Object { $_.Technique -in $objectTechniques }
    $misconfigurationIssues = $allIssues | Where-Object { $_.Technique -in $misconfigurationTechniques }
    $accessIssues = $allIssues | Where-Object { $_.Technique -in $accessTechniques }
    $ownershipIssues = $allIssues | Where-Object { $_.Technique -in $ownershipTechniques }
    
    # Get risky principals
    Write-Verbose "Calculating principal risk scores..."
    $riskyPrincipals = Find-LS2RiskyPrincipal
    
    # Prepare data for tables - ALL tabs show same columns, just filtered/sorted differently
    $standardColumns = @(
        'Technique'
        'Forest'
        'Name'
        'DistinguishedName'
        @{N = 'ObjectClass'; E = { if ($_.ObjectClass) { $_.ObjectClass } else { 'N/A' } } }
        @{N = 'IdentityReference'; E = { if ($_.IdentityReference) { $_.IdentityReference } else { 'N/A' } } }
        @{N = 'IdentityReferenceSID'; E = { if ($_.IdentityReferenceSID) { $_.IdentityReferenceSID } else { 'N/A' } } }
        @{N = 'IdentityReferenceClass'; E = { if ($_.IdentityReferenceClass) { $_.IdentityReferenceClass } else { 'N/A' } } }
        @{N = 'ActiveDirectoryRights'; E = { if ($_.ActiveDirectoryRights) { $_.ActiveDirectoryRights } else { 'N/A' } } }
        @{N = 'AceObjectTypeGUID'; E = { if ($_.AceObjectTypeGUID) { $_.AceObjectTypeGUID } else { 'N/A' } } }
        @{N = 'AceObjectTypeName'; E = { if ($_.AceObjectTypeName) { $_.AceObjectTypeName } else { 'N/A' } } }
        @{N = 'Enabled'; E = { if ($null -ne $_.Enabled) { $_.Enabled } else { 'N/A' } } }
        @{N = 'EnabledOn'; E = { if ($_.EnabledOn) { $_.EnabledOn -join ', ' } else { 'N/A' } } }
        @{N = 'CAFullName'; E = { if ($_.CAFullName) { $_.CAFullName } else { 'N/A' } } }
        @{N = 'Owner'; E = { if ($_.Owner) { $_.Owner } else { 'N/A' } } }
        @{N = 'HasNonStandardOwner'; E = { if ($null -ne $_.HasNonStandardOwner) { $_.HasNonStandardOwner } else { 'N/A' } } }
        @{N = 'Members'; E = { if ($_.MemberCount) { $_.MemberCount } else { 'N/A' } } }
        @{N = 'Issue'; E = { if ($_.Issue) { $_.Issue -replace "`n", "`n`n" } else { 'N/A' } } }
        @{N = 'Fix'; E = { if ($_.Fix) { $_.Fix -replace "`n", "`n`n" } else { 'N/A' } } }
        @{N = 'Revert'; E = { if ($_.Revert) { $_.Revert -replace "`n", "`n`n" } else { 'N/A' } } }
    )
    
    $allIssuesTable = $allIssues | Select-Object $standardColumns
    $templateIssuesTable = $templateIssues | Select-Object $standardColumns
    $caIssuesTable = $caIssues | Select-Object $standardColumns
    $objectIssuesTable = $objectIssues | Select-Object $standardColumns
    $misconfigurationIssuesTable = $misconfigurationIssues | Select-Object $standardColumns
    $accessIssuesTable = $accessIssues | Select-Object $standardColumns
    $ownershipIssuesTable = $ownershipIssues | Select-Object $standardColumns
    
    $principalsTable = $riskyPrincipals | Select-Object `
        Principal,
    IssueCount,
    @{N = 'Techniques'; E = { $_.Techniques -join ', ' } },
    @{N = 'VulnerableObjects'; E = { $_.VulnerableObjects.Count } }
    
    $forestName = if ($script:Forest) { $script:Forest } else { 'Unknown Forest' }
    
    # Define conditional formatting rules - applies consistently to all issue tables
    $issueFormatting = {
        # Template issues (red-purple range)
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC1' -BackgroundColor '#ffcdd2' -Color Black
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC2' -BackgroundColor '#f8bbd0' -Color Black
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC4a' -BackgroundColor '#e1bee7' -Color Black
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC4o' -BackgroundColor '#d1c4e9' -Color Black
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC9' -BackgroundColor '#ce93d8' -Color Black
        
        # CA issues (yellow-orange range)
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC6' -BackgroundColor '#fff59d' -Color Black
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC7a' -BackgroundColor '#ffcc80' -Color Black
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC7m' -BackgroundColor '#ffb74d' -Color Black
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC11' -BackgroundColor '#ff9800' -Color Black
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC16' -BackgroundColor '#ffa726' -Color Black
        
        # Object issues (green-blue range)
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC5a' -BackgroundColor '#a5d6a7' -Color Black
        New-HTMLTableCondition -Name 'Technique' -Value 'ESC5o' -BackgroundColor '#80cbc4' -Color Black
        
        # Rights-based formatting (high severity)
        New-HTMLTableCondition -Name 'ActiveDirectoryRights' -ComparisonType string -Operator like -Value '*GenericAll*' -BackgroundColor '#d32f2f' -Color White
        New-HTMLTableCondition -Name 'ActiveDirectoryRights' -ComparisonType string -Operator like -Value '*WriteDacl*' -BackgroundColor '#ef5350' -Color White
        New-HTMLTableCondition -Name 'ActiveDirectoryRights' -ComparisonType string -Operator like -Value '*WriteOwner*' -BackgroundColor '#ff9800' -Color White
        New-HTMLTableCondition -Name 'ActiveDirectoryRights' -ComparisonType string -Operator like -Value '*WriteProperty*' -BackgroundColor '#ffa726' -Color Black
        New-HTMLTableCondition -Name 'ActiveDirectoryRights' -ComparisonType string -Operator like -Value '*GenericWrite*' -BackgroundColor '#ffa726' -Color Black
        
        # Status-based formatting
        New-HTMLTableCondition -Name 'Enabled' -Value $true -BackgroundColor '#fff9c4' -Color Black
    }
    
    # Define conditional formatting for principals table (different schema)
    # Order matters: most severe conditions last so they override less severe ones
    $principalFormatting = {
        New-HTMLTableCondition -Name 'IssueCount' -ComparisonType number -Operator ge -Value 1 -BackgroundColor '#fdd835' -Color Black
        New-HTMLTableCondition -Name 'IssueCount' -ComparisonType number -Operator ge -Value 5 -BackgroundColor '#ff9800' -Color White
        New-HTMLTableCondition -Name 'IssueCount' -ComparisonType number -Operator gt -Value 10 -BackgroundColor '#ef5350' -Color White
    }
    
    # Generate HTML Dashboard
    New-HTML -TitleText "Locksmith2 Security Dashboard - $forestName" -Online:$Online -FilePath $FilePath -Show:$Show {
        
        # Use tabs for single-page navigation with content switching
        New-HTMLTabStyle -SlimTabs -Transition -SelectorColor Magenta
        
        New-HTMLTab -Name 'All Issues' -IconSolid exclamation-triangle -IconColor Red {
            New-HTMLSection -Invisible {
                New-HTMLPanel -Width 10% {
                    New-HTMLText -Text "All Issues - Expanded Principals ($($allIssues.Count) total)" -FontSize 20 -FontWeight bold
                    New-HTMLText -Text @"
This view shows all discovered AD CS vulnerabilities with group memberships expanded to individual principals.
Issues are marked with the ESC technique and show which principals can exploit each configuration.
"@
 -Color '#888' -FontSize 14
                }
                New-HTMLPanel {
                    New-HTMLTable -DataTable $allIssuesTable `
                        -Filtering `
                        -PagingLength 25 `
                        -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'searchBuilder', 'searchPanes') `
                        -Title 'All AD CS Security Issues' {& $issueFormatting}
                }
            }
        }
        
        New-HTMLTab -Name 'Templates' -IconSolid file-contract -IconColor Orange {
            New-HTMLSection -Invisible {
                New-HTMLPanel -Width 10% {
                    New-HTMLText -Text "Certificate Template Issues ($($templateIssues.Count) issues)" -FontSize 20 -FontWeight bold
                    New-HTMLText -Text @"
Certificate templates are the most common source of AD CS vulnerabilities. These issues allow principals to request
certificates with dangerous permissions, subject alternative names, or enrollment agent capabilities.
"@
 -Color '#888' -FontSize 14
                }
                New-HTMLPanel {
                    New-HTMLTable -DataTable $templateIssuesTable `
                        -Filtering `
                        -PagingLength 25 `
                        -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'searchBuilder', 'searchPanes') `
                        -Title 'Template Vulnerabilities' `
                        -DefaultSortColumn 'Technique' {& $issueFormatting}
                }
            }
        }
        
        New-HTMLTab -Name 'CAs' -IconSolid certificate -IconColor Yellow {
            New-HTMLSection -Invisible {
                New-HTMLPanel -Width 10% {
                    New-HTMLText -Text "Certification Authority Issues ($($caIssues.Count) issues)" -FontSize 20 -FontWeight bold
                    New-HTMLText -Text @"
CA-level issues involve dangerous role assignments (ESC7) or insecure CA configurations (ESC6, ESC11, ESC16).
These vulnerabilities grant principals excessive control over certificate issuance.
"@
 -Color '#888' -FontSize 14
                }
                New-HTMLPanel {
                    New-HTMLTable -DataTable $caIssuesTable `
                        -Filtering `
                        -PagingLength 25 `
                        -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'searchBuilder', 'searchPanes') `
                        -Title 'CA Configuration Issues' `
                        -DefaultSortColumn 'Name' {& $issueFormatting}
                }
            }
        }
        
        New-HTMLTab -Name 'Objects' -IconSolid folder -IconColor Blue {
            New-HTMLSection -Invisible {
                New-HTMLPanel -Width 10% {
                    New-HTMLText -Text "PKI Object Issues ($($objectIssues.Count) issues)" -FontSize 20 -FontWeight bold
                    New-HTMLText -Text @"
ESC5 vulnerabilities involve dangerous ownership or write permissions on PKI infrastructure objects.
These allow principals to modify templates, CAs, or other critical AD CS components.
"@
 -Color '#888' -FontSize 14
                }
                New-HTMLPanel {
                    New-HTMLTable -DataTable $objectIssuesTable `
                        -Filtering `
                        -PagingLength 25 `
                        -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'searchBuilder', 'searchPanes') `
                        -Title 'Infrastructure Object Issues' `
                        -DefaultSortColumn 'Name' {& $issueFormatting}
                }
            }
        }
        
        New-HTMLTab -Name 'Risky Principals' -IconSolid user-shield -IconColor Purple {
            New-HTMLSection -Invisible {
                New-HTMLPanel -Width 10% {
                    New-HTMLText -Text "Principal Risk Analysis ($($principalsTable.Count) principals)" -FontSize 20 -FontWeight bold
                    New-HTMLText -Text @"
This analysis shows which principals have access to the most AD CS vulnerabilities. Principals with high issue counts
represent concentrated risk and should be prioritized for remediation or monitoring.
"@
 -Color '#888' -FontSize 14
                }
                New-HTMLPanel {
                    New-HTMLTable -DataTable $principalsTable `
                        -Filtering `
                        -PagingLength 25 `
                        -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'searchBuilder', 'searchPanes') `
                        -Title 'Principals by Risk Score' `
                        -DefaultSortColumn 'IssueCount' `
                        -DefaultSortOrder Descending {& $principalFormatting}
                }
            }
        }
        
        New-HTMLTab -Name 'Misconfigurations' -IconSolid cog -IconColor Red {
            New-HTMLSection -Invisible {
                New-HTMLPanel -Width 10% {
                    New-HTMLText -Text "Misconfiguration Issues ($($misconfigurationIssues.Count) issues)" -FontSize 20 -FontWeight bold
                    New-HTMLText -Text @"
These vulnerabilities result from insecure template or CA configurations that allow certificate abuse.
Examples include weak enrollment restrictions (ESC1, ESC2), SubCA attacks (ESC6), or weak certificate mappings (ESC9).
"@
 -Color '#888' -FontSize 14
                }
                New-HTMLPanel {
                    New-HTMLTable -DataTable $misconfigurationIssuesTable `
                        -Filtering `
                        -PagingLength 25 `
                        -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'searchBuilder', 'searchPanes') `
                        -Title 'Configuration-Based Vulnerabilities' `
                        -DefaultSortColumn 'Technique' {& $issueFormatting}
                }
            }
        }
        
        New-HTMLTab -Name 'Access Control' -IconSolid key -IconColor Green {
            New-HTMLSection -Invisible {
                New-HTMLPanel -Width 10% {
                    New-HTMLText -Text "Dangerous Access Control Issues ($($accessIssues.Count) issues)" -FontSize 20 -FontWeight bold
                    New-HTMLText -Text @"
These vulnerabilities involve principals with excessive write or modify permissions on templates or PKI objects.
ESC4a and ESC5a allow principals to modify certificate templates or infrastructure to create exploitable configurations.
"@
 -Color '#888' -FontSize 14
                }
                New-HTMLPanel {
                    New-HTMLTable -DataTable $accessIssuesTable `
                        -Filtering `
                        -PagingLength 25 `
                        -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'searchBuilder', 'searchPanes') `
                        -Title 'Write/Modify Permission Issues' `
                        -DefaultSortColumn 'ActiveDirectoryRights' {& $issueFormatting}
                }
            }
        }
        
        New-HTMLTab -Name 'Ownership' -IconSolid crown -IconColor Gold {
            New-HTMLSection -Invisible {
                New-HTMLPanel -Width 10% {
                    New-HTMLText -Text "Non-Standard Ownership Issues ($($ownershipIssues.Count) issues)" -FontSize 20 -FontWeight bold
                    New-HTMLText -Text @"
These vulnerabilities involve templates or PKI objects owned by non-standard principals.
Owners have full control and can modify or delete objects. ESC4o and ESC5o identify dangerous ownership configurations.
"@
 -Color '#888' -FontSize 14
                }
                New-HTMLPanel {
                    New-HTMLTable -DataTable $ownershipIssuesTable `
                        -Filtering `
                        -PagingLength 25 `
                        -Buttons @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'searchBuilder', 'searchPanes') `
                        -Title 'Dangerous Ownership Configurations' `
                        -DefaultSortColumn 'Owner' {& $issueFormatting}
                }
            }
        }
    }
    
    Write-Verbose "Dashboard generated: $FilePath"
    if (-not $Show) {
        Write-Host "Dashboard saved to: $FilePath"
    }
}