HardenSysvol.psm1

 <#
    .SYNOPSIS
    Scans the SYSVOL folder for sensitive or risky content.
 
    .DESCRIPTION
    This module audits the SYSVOL directory for files containing sensitive data (passwords, usernames, certificates, configs, etc.).
    It helps identify security risks and integrity issues to harden Active Directory environments.
   
    .VERSION
    2.1
 
    .Contribution
    Credit : HardenAD Community HardenAD
    Credit : It-connect Community It-Connect
 
    .AUTHOR
    DAKHAMA Mehdi
 
    .PARAMETER dnsDomain
    Specifies the DNS domain to be scanned. Defaults to the current user's DNS domain if not provided.
 
    .PARAMETER ignoreExtensions
    Specifies file extensions to ignore during the scan.
 
    .EXAMPLE
    # Scan the Sysvol folder of the current domain
    Invoke-HardenSysvol
 
    # Scan the Sysvol folder of a specific domain, ignoring .txt and .log files
    Invoke-HardenSysvol -dnsDomain "example.com" -ignoreExtensions "txt", "log" -Addpattern admin -AddExtensions adml,admx,adm
 
.NOTES
    This script not requires administrative privileges to access and scan the Sysvol directory.
 
.LINK
    https://github.com/dakhama-mehdi/Harden-Sysvol
    #>


function Invoke-HardenSysvol {
    <#
    .SYNOPSIS
    Scans the SYSVOL folder for sensitive or risky content.
 
    .DESCRIPTION
    This module audits the SYSVOL directory for files containing sensitive data (passwords, usernames, certificates, configs, etc.).
    It helps identify security risks and integrity issues to harden Active Directory environments.
 
    .PARAMETER dnsDomain
    Specifies the DNS domain to be scanned. Defaults to the current user's DNS domain if not provided.
 
    .PARAMETER ignoreExtensions
    Specifies file extensions to ignore during the scan.
 
    .EXAMPLE
    Scan the Sysvol folder of the current domain
    Invoke-HardenSysvol
    .EXAMPLE
    Scan the Sysvol folder of a specific domain, ignoring .txt and .log files
    Invoke-HardenSysvol -dnsDomain "example.com" -ignoreExtensions "txt","log" -Addpattern admin -AddExtensions adml,admx,adm
    .EXAMPLE
    Scan include all extensions
    Invoke-HardenSysvol -Allextensions
    .EXAMPLE
    Scan include Policydefintions
    Invoke-HardenSysvol -Allextensions -Includepolicydefinitions
 
    .NOTES
    This script not requires administrative privileges to access and scan the Sysvol directory.
 
    .LINK
    https://github.com/dakhama-mehdi/Harden-Sysvol
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [String]$dnsDomain = $env:USERDNSDOMAIN,
        
        [Parameter(Mandatory = $false)]
        [String[]]$ignoreExtensions,

        [Parameter(Mandatory = $false)]
        [String[]]$AddExtensions,

        [Parameter(Mandatory = $false)]
        [String[]]$Addpattern,
        
        [Parameter(Mandatory = $false)]
        [String[]]$removepattern,        

        #Scann all extensions
        [Parameter(ValueFromPipeline = $true, HelpMessage = "Scann all extension")]
        [switch]$Allextensions,

        #Location the report will be saved
        [Parameter(ValueFromPipeline = $true, HelpMessage = "Enter desired directory path to save; Default: %temp%")]
        [String]$SavePath = $env:TEMP,

        #Location to export CSV
        [Parameter(ValueFromPipeline = $true, HelpMessage = "Enter desired directory path to save; Default: %temp%")]
        [String]$Exportcsv,

        #Location the report will be saved
        [Parameter(ValueFromPipeline = $true, HelpMessage = "maximum size of the file to be considered large in MB")]
        [int]$Maxfilesize = '10',

        #Location the report will be saved
        [Parameter(ValueFromPipeline = $true, HelpMessage = "maximum size of the binary to be considered large in MB")]
        [String]$MaxBinarysize = '50',

        # Include: scans the 'PolicyDefinitions' folder, which contains added .admx and .adml templates
        [Parameter(HelpMessage = "Include files from the 'Policies\\PolicyDefinitions' folder (central store for ADMX/ADML templates).")]
        [switch]$IncludePolicyDefinitions,

        # Checks all shared folders on each domain controller and lists custom shares (excluding default and DFS links)
        [Parameter(HelpMessage = "Scan all shares on all domain controllers and list non-default ones (skipping DFS links)")]
        [switch]$CheckShares,

        #Option to use if no want to start automatically hmtl page, used on script.
        [Parameter(ValueFromPipeline = $true, HelpMessage = "No auto start html page")]
        [switch]$Silent,

        #Location Custom pattern file
        [Parameter(ValueFromPipeline = $true, HelpMessage = "Enter Custome XML file path; Default: %module%\patterns.xml")]
        [String]
        [ValidateScript({ 
        if ($_ -match '\.xml$') { 
        return $true 
        } else { 
        throw "The file must have a .xml extension."
         }
        })]$Custompatterns
    )

$Bannercolor = "Yellow"
$MSGcolor    = "DarkGreen"

function Show-HardenSysvolBanner {
   param (
        [string]$BannerColor
    )
Write-Host ""
Write-Host "╔═════════════════════════════════════════════════════╗" -ForegroundColor $Bannercolor
Write-Host "║ Welcome to HardenSysvol v2.3.0 ║" -ForegroundColor $Bannercolor
Write-Host "║ Auditing Active Directory SYSVOL & GPOs ║" -ForegroundColor $Bannercolor
Write-Host "║ Developed by HardenAD Community ║" -ForegroundColor $Bannercolor
Write-Host "╚═════════════════════════════════════════════════════╝" -ForegroundColor $Bannercolor
Write-Host ""
}
Show-HardenSysvolBanner -BannerColor $Bannercolor
#region script
#region code

#region load prerequist


# Test access to the share
$sysvolRoot = "\\$dnsDomain\sysvol\"
if ((Test-Path $sysvolRoot) -eq $false) {
    # Try check for logon server
    $sysvolRoot = "$env:LOGONSERVER\sysvol"
    if ((Test-Path $sysvolRoot) -eq $false) {
    throw "Cannot access domain or share, pls check with Get-Childrenitem $dnsDomain"
    } 
} 

# Test Modules
$modulesToCheck = @("PSWriteOffice", "PSWritePDF", "PSWriteHTML")

foreach ($module in $modulesToCheck) {
    try {
        # Check if module installed
        if (!(Get-Module -ListAvailable -Name $module)) {
            Write-Output "Installing module: $module."
            install-Module -Name $module -Force -Scope CurrentUser -ErrorAction Ignore
            Write-Output "The module $module has been successfully installed"
        } 
    } catch {
       Write-Error "Error while installing module $module : $_"
       throw "Script stopped due to an error during module installation $module."
    }
}

# Check about Word office if installed or not to read old version doc,xls
function Is-WordInstalled {
    try {
        $wordApp = New-Object -ComObject Word.Application
        # If the COM object is created successfully, Word is installed
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wordApp) | Out-Null
        return $true
    } catch {
        return $false
    }
}

# Checkif 7zip is installed to read rar,7z protected file by password or encrypted
function Is-7zip {
$sevenZipRegPath = "HKLM:\SOFTWARE\7-Zip"
$sevenZipPath = Get-ItemProperty -Path $sevenZipRegPath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path

if (-not $sevenZipPath) {
    $sevenZipRegPath = "HKCU:\Software\7-Zip"  #Check in HKCU
    $sevenZipPath = Get-ItemProperty -Path $sevenZipRegPath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
}

if ($sevenZipPath) {
    return $sevenZipPath
} else {
    return $false
}
}

    $wordinstalled = Is-WordInstalled
    $zipinstalled  = Is-7zip

    # Removing ghost runspaces
    $PreviousRS = get-runspace | where-object {($_.id -ne 1)} 
    if ($PreviousRS) { $PreviousRS.dispose() }

    $Results =  $null
    # Script start: obtain the current date and time
    $startDate = Get-Date
    
    #region getxmlcontenant
    # Retrieve file extensions from the XML file
    $xmlFileExtensions =  Join-Path -Path $PSScriptRoot -ChildPath "file_extensions.xml"
        
    $extensionsXML = [xml](Get-Content $xmlFileExtensions -Encoding UTF8)

    if (!$Allextensions) {
    $fileExtensions = $extensionsXML.root.FileExtensions.Extension
    } else {
    $fileExtensions = "*.*"
    }

    if ($AddExtensions) {

    $fileExtensions = [System.Collections.ArrayList]@($fileExtensions)

    $AddExtensions | ForEach-Object {
    $fileExtensions.Add("*." + $_)
    }

    }

    # Retrieve password patterns from the XML file
    if ($Custompatterns) {
    try {
    $CustomextensionsXML = [xml](Get-Content $Custompatterns -Encoding UTF8)
    $passwordPatterns = $CustomextensionsXML.root.PasswordPatterns.Pattern
    } catch {
    Write-Error "Error while reading the custom patterns file: $_"
    throw "The custom patterns file could not be found or is invalid."
    }
    } 
    else {
    $passwordPatterns = $extensionsXML.root.PasswordPatterns.Pattern
    }

    if ($passwordPatterns.Count -eq 0) {
    throw "The custom patterns file could not be found or is invalid."
    }
    if ($removepattern) {

    $passwordPatterns = [System.Collections.ArrayList]@($passwordPatterns)

    $PatternAliases = @{
    ipv4     = '\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b'
    email    = '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
    MD5      = '\b[a-fA-F0-9]{32}\b'
    Sha1     = '\b[a-fA-F0-9]{40}\b'
    Sha256   = '\b[a-fA-F0-9]{64}\b'
    }

    # remplace Alias by real regex
    $removepattern | ForEach-Object {
        if ($PatternAliases.ContainsKey($_)) {
            #$PatternAliases[$_]
            $passwordPatterns.Remove($PatternAliases[$_].trim())
        } 
    }  
    

    $removepattern | ForEach-Object {
        $patternToRemove = $_
        $patternsToRemove = $passwordPatterns | Where-Object { $_ -like "*$patternToRemove*" }        
        foreach ($pattern in $patternsToRemove) {
            $passwordPatterns.Remove($pattern)
        }
    }
}
    if ($Addpattern) {
    $passwordPatterns = [System.Collections.ArrayList]@($passwordPatterns)

    $Addpattern | ForEach-Object {
    $passwordPatterns.Add($_)
    }
    }

    if ($ignoreExtensions) {
        $ignoreExtensions = $ignoreExtensions | ForEach-Object {
        "*." + $_
    }
    }

    #get binary sign from json and load module path
    $module  = Join-Path -Path $PSScriptRoot\Privates -ChildPath "FileHandlers.psm1"
    $jsonfile = Join-Path -Path $PSScriptRoot -ChildPath "extensions.json"
    $htmlfunction = Join-Path -Path $PSScriptRoot\Privates -ChildPath "Functioncheck.psm1"

    try {
    $jsonContent = Get-Content -Path $jsonfile -Raw | ConvertFrom-Json
    } catch {
    throw "Script stopped due to an error during json file $_ "
    }

    # Initialize a list to store the results
    $Results = @()

    # Informations for check ACL in sysvol

    # Get base SID
    $baseSID = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value -replace '-\d+$',''

    # Default accounts sid
    $sids = @(
    'S-1-5-32-544', # Administrators
    'S-1-5-18',     # SYSTEM
    'S-1-3-0',      # CREATOR
    'S-1-5-32-549', # Server Operator
    "$baseSID-512", # Domain Admins
    "$baseSID-519", # Entreprise Admins
    "$baseSID-520", # Creator GPO
    'S-1-5-9'       # Enterprise DCs
)
    
    # Trying get list default ACL users in sysvol
    $Trustgroups = $sids | ForEach-Object {
    $sid = New-Object System.Security.Principal.SecurityIdentifier($_)
    try {
        $sid.Translate([System.Security.Principal.NTAccount]).Value
    } catch {
        $sid.value  # $sid.Value Check SID if not resolve by machine
    }
}

    # Permission Trust
    $Permissiontrust = @(
    '-1610612736'
    'ReadAndExecute', 'Read', 'Synchronize', 
    'ReadAndExecute, Synchronize', 'Read, Synchronize',
    'ReadAttributes, ReadExtendedAttributes, ReadPermissions, Synchronize'
    )

    #endregion getxmlcontenant

#Initialize the variables
$notAccessibleFiles = $fichiertraite = $Results = $null
$pool = $runspaces = $null

# Pool definition (creation of slots)
$pool = [RunspaceFactory]::CreateRunspacePool(1,10)
$pool.ApartmentState = "MTA"
$pool.Open()
$runspaces = @()

#endregion load prerequist

# Region Scriptfunction
$scriptblock = {
    Param (
        [object[]]$sysfiles1,
        [string[]]$passwordPatterns,
        [string]$wordinstalled,
        [string]$zipinstalled,
        [string]$module,
        [int]$Maxfilesize,
        [int]$MaxBinarysize,
        [object]$jsonContent,
        [string[]]$Trustgroups,      
        [string[]]$Permissiontrust
    )

    # Import modul FileHandlers.psm1
    Import-Module $module -Force -Verbose
    $sysfiles = $sysfiles1.fullname
    $aclResults = Check-SysvolAcl -FilePath $sysfiles -TrustGroups $Trustgroups -PermissionTrust $Permissiontrust
    if ($aclResults) {
    $aclResults = $aclResults | ForEach-Object {
        $_ | Add-Member -MemberType NoteProperty -Name Created -Value $sysfiles1.CreationTime -Force
        $_
    }
    return $aclResults
    }

    [String]$detectedType =  Get-FileType -filePath $sysfiles -jsonContent $jsonContent -maxfilesize $Maxfilesize -maxbinarysize $MaxBinarysize 
    # Function to search pattern by extensions
    switch ($detectedType)  {
    'docx' {
        $results = Get-DocxContent -filePath $sysfiles -patterns $passwordPatterns
    }
    'xlsx' {
        $results = Get-XlsxContent -filepath $sysfiles -patterns $passwordPatterns
    }
    {$_ -in "xlsm","xlam"} {
        $results = Get-Xlsmcontent -filepath $sysfiles
    }
    'pptx' {
        $results = Get-PPTContent -filepath $sysfiles -patterns $passwordPatterns -wordinstalled $wordinstalled
    }
    'doc' {
        $results = Get-DocContent -filepath $sysfiles -patterns $passwordPatterns -wordinstalled $wordinstalled
    }
    'xls' {
        $results = Get-XlsContent -filepath $sysfiles -patterns $passwordPatterns -wordinstalled $wordinstalled
    }
    {$_ -in "odp","ods","odt"} {
        $results = Get-OdsContent -filepath $sysfiles -patterns $passwordPatterns # Même traitement que ods
    }
    'pdf' {
        $results = Get-PdfContent -filepath $sysfiles -patterns $passwordPatterns
    }
    'xml' {
        $results = Get-XmlContent -filepath $sysfiles -patterns $passwordPatterns
    }
    {$_ -in "exe","dll","msi","msu","cab"} {
        $results = Get-ExecutablesContent -filepath $sysfiles   
    }
    {$_ -in "cer","crt","cert","der","pfx","pem"} {
        $results = Get-CertifContent -filepath $sysfiles
    }
    {$_ -in "p7b","p7c","p7s"} {
        $results = Get-P7bCertContent -filepath $sysfiles
    }
    {$_ -in "bmp","webp","ico","bmp","tif"} {
        $results =  Get-HiddenFilesInImage -filepath $sysfiles
    }
    {$_ -in "jpg","jpeg","png","gif"} {
        $results =  Get-HiddenFilesSpecificInImage -filepath $sysfiles
    }    
    {$_ -in "zip","7z","rar"} {
        $results =  Get-Zipprotectedbypass -filepath $sysfiles -zipinstalled $zipinstalled
    }
    'requires_check' {
        $results = Get-RequiredCheckContent -filepath $sysfiles
    }
    'others' {
        $results = Get-OthersContent -filepath $sysfiles -patterns $passwordPatterns
    }
    'bigsize' {
        $results = Get-checkfilesize -filepath $sysfiles 
    }
    'DefaultGPO' {
        $results = Get-DefaultGPOContent -filepath $sysfiles -patterns $passwordPatterns
    }
}    
    # Execute the appropriate command based on the detected file type
    if ($results) {
    $results | Add-Member -MemberType NoteProperty -Name Created -Value $sysfiles1.CreationTime -Force
    return $results
    } 
}

if (Is-WordInstalled) {
# Terminate the running Word and Excel processes to prevent double opening
Get-process *winword* -erroraction SilentlyContinue | Stop-Process
Get-Process excel -erroraction SilentlyContinue | Stop-Process
}

$fichiertraite = 0
# Define the array to store inaccessible files
#[System.Collections.Generic.List[Object]]$notAccessibleFiles = @()
$notAccessibleFiles = [System.Collections.Generic.List[object]]::new()

 function Test-SysvolRootStructure {
    param(
        [string]$Sysvolroot
    )

    #$sysvolRoot = "\\$DomainName\SYSVOL"

    Write-Host "`n Found SYSVOL root: $sysvolRoot" -ForegroundColor Cyan

    $rootContent = Get-ChildItem -Path $sysvolRoot -Directory -Force -ErrorAction SilentlyContinue

    if (-not $rootContent) {
        Write-Warning "No folders found at the SYSVOL root. Check permissions or connectivity."
        return
    }

    $table = $rootContent | Sort-Object LastWriteTime | ForEach-Object {
    [PSCustomObject]@{
        Name          = $_.Name
        Type          = if ($_.Attributes -match 'ReparsePoint') { "Linked (DFS)" } else { "Directory" }
        LastWriteTime = $_.LastWriteTime.ToString("yyyy-MM-dd HH:mm")        
    }
    }
    $table | Format-Table -AutoSize | Out-String | Write-Host

    # Unexpected folders
    $expected = @("scripts", "Policies", "$dnsDomain")
    $unexpected = $rootContent | Where-Object { $_.Name -notin $expected }
    $Trustpath = ($rootContent | ? { $_.name -eq $dnsDomain}).fullname

    if ($unexpected) {
        Write-Warning "Unexpected folders detected in SYSVOL root:" 
        foreach ($folder in $unexpected) {
            Write-Host " - $($folder.FullName)" -ForegroundColor Red
            $notAccessibleFiles.Add([PSCustomObject]@{ Error = "Unexpected folder found in SYSVOL: $($folder.FullName)" })
        }
    }
    else {
        #Write-Host "`n SYSVOL root structure looks normal." -ForegroundColor Green
    }

    return $Trustpath
} 

 #$sysvolRoot = "\\$DomainName\SYSVOL"
 $sysvolpath = Test-SysvolRootStructure -Sysvolroot $sysvolRoot
 Start-Sleep -Seconds 2
 clear
 Show-HardenSysvolBanner -BannerColor $Bannercolor
 Write-Host "[Step 1/4] Search files in $sysvolpath ..." -ForegroundColor $MSGcolor
 if (-not $IncludePolicyDefinitions) {
 $Allfiles = Get-ChildItem -Path $sysvolpath -Recurse -File -Include $fileExtensions -Exclude $ignoreExtensions -Force -ErrorAction SilentlyContinue -ErrorVariable notacess | Where-Object { $_.FullName -notmatch '\\Policies\\PolicyDefinitions\\' }
 } else {
 $Allfiles = Get-ChildItem -Path $sysvolpath -Recurse -File -Include $fileExtensions -Exclude $ignoreExtensions -Force -ErrorAction SilentlyContinue -ErrorVariable notacess
 }

 Write-Host "Found $($Allfiles.Count) file(s) to analyze." -ForegroundColor $MSGcolor 
 Write-Host "[Step 2/4] Starting scan ..." -ForegroundColor $MSGcolor
 $Allfiles | ForEach-Object {

if ($notacess) { 
    #Write-Host $notacess -ForegroundColor Red
    $notacess.GetEnumerator() | ForEach-Object {
    $errorDetails = [PSCustomObject]@{
            Error    = $_
        }
        }
        $notAccessibleFiles.Add($errorDetails)
        $notacess.Clear()
 } 
 else {
 
$fichiertraite++
$sysfiles1 = [PSCustomObject]@{
            FullName     = $_.FullName
            CreationTime = ($_.CreationTime.ToString("yyyy-MM-dd"))
        }

#clear
#Write-Host scan : $sysfiles1.FullName -ForegroundColor Cyan
#$keepscrenn += "scans :" + $sysfiles1.FullName

$runspace = [PowerShell]::Create()
$null = $runspace.AddScript($scriptblock)
$null = $runspace.AddArgument($sysfiles1)
$null = $runspace.AddArgument($passwordPatterns)
$null = $runspace.AddArgument($wordinstalled)
$null = $runspace.AddArgument($zipinstalled)
$null = $runspace.AddArgument($module)
$null = $runspace.AddArgument($Maxfilesize)
$null = $runspace.AddArgument($MaxBinarysize)
$null = $runspace.AddArgument($jsonContent)
$null = $runspace.AddArgument($Trustgroups)       
$null = $runspace.AddArgument($Permissiontrust)   
$runspace.RunspacePool = $pool
$runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() }
}
}
 Write-Host "[Step 3/4] Starting analyze ..." -ForegroundColor $MSGcolor

function Show-CustomProgressBar {
    param (
        [int]$Completed,
        [int]$Total,
        [TimeSpan]$Elapsed
    )

    $percent = if ($Total -gt 0) { [math]::Round(($Completed / $Total) * 100) } else { 0 }
    $barLength = 30
    $filledLength = [math]::Round(($percent / 100) * $barLength)

    $bar = ('#' * $filledLength).PadRight($barLength, '.')
    Write-Host "`rProcessed $Completed in $Total files [$bar] $percent% | Elapsed : $($elapsed.ToString("hh\:mm\:ss")) " -ForegroundColor Cyan -NoNewline

}
    
    $timer = [System.Diagnostics.Stopwatch]::StartNew()
    $total = $runspaces.Count

while ($runspaces.Status -ne $null) {
    Start-Sleep -Seconds 1

    $completed = @()
    $slt_remaining = 0
    foreach ($r in $runspaces) {
    if ($r.Status -ne $null) {
        if ($r.Status.IsCompleted) {
            $completed += $r
        } else {
            $slt_remaining++
        }
    }
    }
    $slt_completed = $total - $slt_remaining
    $elapsed = $timer.Elapsed
    $percent = [math]::Round(($slt_completed / $total) * 100)
        
    Show-CustomProgressBar -Completed $slt_completed -Total $total -Elapsed $elapsed 

    foreach ($runspace in $completed) {
        $Results += $runspace.Pipe.EndInvoke($runspace.Status)
        $runspace.Status = $null
    }

    if (($runspaces | Where-Object { $_.Status -ne $null }).Count -eq 0) {
        Write-Progress -Activity "Scan completed" -Completed
        break
    }
}


#endregion code

#region summary
Write-Host "`n[Step 3/4] Scan completed, calculating statistics ..." -ForegroundColor $MSGcolor

# Sort results in unique mode and Skip default GPO policy password settings
$Results = $Results | Select-Object -Unique FilePath, pattern, Reason, Created

$sortedGroups = $Results.filepath | Group-Object | Sort-Object -Property Count -Descending 

# Select the first 5 Path
#$top5path = $sortedGroups | Select-Object Name,Count -First 5

$top5path = $sortedGroups | Select-Object Name,Count -First 5 | ForEach-Object {
    $_.Name = $_.Name -replace '^.*\\', ''
    $_
}

# Top 5 words
$top5Words= $Results.pattern | Group-Object | Sort-Object -Property Count -Descending | Select-Object Name,count -First 5

#Number found objects
$Objectfound = 0
$Objectfound = $sortedGroups.Count

# Group file paths by file extension
$top5extensions = $sortedGroups.name | Group-Object -Property { ($_ -split "\.")[-1] } | select Name,Count -First 5

# End of the script: obtain the current date and time
$endDate = Get-Date

# Calculate the time difference
$elapsedTime = New-TimeSpan -Start $startDate -End $endDate
$elapsedTime = $($elapsedTime.ToString("hh\:mm\:ss"))

#region Calcul potentiel risk
# Assume that $top5Groups, $Allwords, and $Objectfound are already defined"

function Get-RiskClassification {
    param([string]$PatternName)
    switch -Regex ($PatternName) {
        "AutoLogon|cpassword|WrongACL|Suspicious Image|Certificate private key|apikey|token|ssh-key|private-key" {
            return [PSCustomObject]@{ Level = 'Critical'; Weight = 7 }
        }
        "SHA-1|SHA-256|md5|check required|Macros detected" {
            return [PSCustomObject]@{ Level = 'High'; Weight = 5 }
        }
        "Password|Pass|NotSigned|\bpass\b|\bpwd\b|auth|\bauth\b|mdp|\bmdp\b|credential|\blogin\b|\\blogin\\b|securestring|runas|accesskey|identifiant" {
            return [PSCustomObject]@{ Level = 'Medium'; Weight = 3 }
        }
        "net use|net user|WrongParameters|Zip protected" {
            return [PSCustomObject]@{ Level = 'Medium'; Weight = 3 }
        }
        "credentials|\bsecret\b|IPv4|Certificate Empty|UPN|Protected Certificate" {
            return [PSCustomObject]@{ Level = 'Low'; Weight = 2 }
        }
        default {
            return [PSCustomObject]@{ Level = 'Info'; Weight = 0 }
        }
    }
}

$ResultsRenamed = $Results | ForEach-Object {
    $risk = Get-RiskClassification -PatternName $_.Pattern
    [PSCustomObject]@{
        FilePath   = $_.FilePath
        Reason     = $_.Pattern
        Value      = $_.Reason
        RiskLevel  = $risk.Level
        RiskWeight = $risk.Weight
        Created    = $_.Created
    }
}

$riskCounts = $ResultsRenamed | Group-Object -Property RiskLevel | Select-Object Name, Count


$totalRisk = 0

if ($Objectfound -gt 30) {
    $totalRisk += 10
} else {
    $totalRisk += ($Objectfound / 30) * 10
}

$totalRisk += ($ResultsRenamed | Measure-Object -Property RiskWeight -Sum).Sum

# Limited score to 100%
if ($totalRisk -gt 100) {
    $totalRisk = 100
} else {
    $totalRisk = [int]$totalRisk
}

$PatternFamilyMap = @{
    'cpassword'           = 'Fuite de credentials'
    'AutoLogon'           = 'Fuite de credentials'
    'Password'            = 'Fuite de credentials'
    'Pass'                = 'Fuite de credentials'
    'mdp'                 = 'Fuite de credentials'
    'auth'                = 'Fuite de credentials'
    'credential'          = 'Fuite de credentials'
    'login'               = 'Fuite de credentials'
    'Net Credentials'     = 'Fuite de credentials'
    'securestring'        = 'Fuite de credentials'
    'apikey'              = 'Fuite de credentials'
    'token'               = 'Fuite de credentials'
    'ssh-key'             = 'Fuite de credentials'
    'private-key'         = 'Fuite de credentials'
    'runas'               = 'Fuite de credentials'
    'accesskey'           = 'Fuite de credentials'
    'identifiant'         = 'Fuite de credentials'

    'WrongACL'            = 'Permissions ACL'

    'check required'      = 'Fichiers suspects'
    'Suspicious Image'    = 'Fichiers suspects'
    'NotSigned'           = 'Fichiers suspects'

    'Certificate private key'     = 'Certificats'
    'Certificate Empty'           = 'Certificats'
    'Protected Certificate'       = 'Certificats'

    'Macros detected'     = 'Documents actifs'
    'Zip protected'       = 'Documents actifs'

    'IPv4'                = 'Exposition réseau'
    'Commande Net User'   = 'Exposition réseau'

    'SHA-1'               = 'Hashs en clair'
    'SHA-256'             = 'Hashs en clair'
    'MD5'                 = 'Hashs en clair'

    'UPN'                 = 'Identité'
}


function Get-PatternFamily {
    param([string]$PatternName)
    if ($PatternFamilyMap.ContainsKey($PatternName)) {
        return $PatternFamilyMap[$PatternName]
    }
    return 'Autre'
}

function Get-ScanMetrics {
    param(
        [object[]]$ResultsRenamed,
        [int]$TotalRisk,
        [int]$TotalFilesScanned
    )

    $riskCounts = $ResultsRenamed | Group-Object -Property RiskLevel | Select-Object Name, Count

    $filesWithFindings = ($ResultsRenamed | Select-Object -ExpandProperty FilePath -Unique).Count
    $density = if ($TotalFilesScanned -gt 0) {
        [math]::Round(($filesWithFindings / $TotalFilesScanned) * 100, 1)
    } else { 0 }

    # Famille dominante parmi Critical/High uniquement
    $criticalHighResults = $ResultsRenamed | Where-Object { $_.RiskLevel -in @('Critical','High') }
    $criticalHighFamilies = $criticalHighResults |
        ForEach-Object { Get-PatternFamily -PatternName $_.Reason } |
        Group-Object | Sort-Object Count -Descending

    $topFamiliesCritHigh = @()
    if ($criticalHighFamilies) {
        $maxCount = $criticalHighFamilies[0].Count
        $topFamiliesCritHigh = $criticalHighFamilies | Where-Object { $_.Count -eq $maxCount } | Select-Object -ExpandProperty Name
    }

    # Famille dominante toutes sévérités confondues (contexte global)
    $allFamilies = $ResultsRenamed |
        ForEach-Object { Get-PatternFamily -PatternName $_.Reason } |
        Group-Object | Sort-Object Count -Descending

    $topFamiliesGlobal = @()
    if ($allFamilies) {
        $maxCountGlobal = $allFamilies[0].Count
        $topFamiliesGlobal = $allFamilies | Where-Object { $_.Count -eq $maxCountGlobal } | Select-Object -ExpandProperty Name
    }

    return [PSCustomObject]@{
        TotalRisk          = $TotalRisk
        RiskCounts          = $riskCounts
        CriticalCount        = ($riskCounts | Where-Object Name -eq 'Critical').Count
        HighCount            = ($riskCounts | Where-Object Name -eq 'High').Count
        MediumCount          = ($riskCounts | Where-Object Name -eq 'Medium').Count
        LowCount             = ($riskCounts | Where-Object Name -eq 'Low').Count
        InfoCount            = ($riskCounts | Where-Object Name -eq 'Info').Count
        FilesWithFindings    = $filesWithFindings
        TotalFilesScanned    = $TotalFilesScanned
        DensityPercent       = $density
        TopFamiliesCritHigh  = $topFamiliesCritHigh
        TopFamiliesGlobal    = $topFamiliesGlobal
    }
}

function Get-RiskNarrative {
    param([PSCustomObject]$Metrics)

    $critCount = $Metrics.CriticalCount
    $highCount = $Metrics.HighCount
    $density   = $Metrics.DensityPercent
    $score     = $Metrics.TotalRisk

    # Cas 1 : Critical présent
    if ($critCount -gt 0) {
        $families = $Metrics.TopFamiliesCritHigh -join ' et '
        return "Éléments critiques détectés ($critCount Critical) — majoritairement $families. Remédiation immédiate requise, indépendamment du score global ($score/100)."
    }

    # Cas 2 : High présent, densité forte (>30%)
    if ($highCount -gt 0 -and $density -gt 30) {
        $families = $Metrics.TopFamiliesCritHigh -join ' et '
        return "Aucun Critical, mais $highCount findings High répartis sur $density% des fichiers scannés — majoritairement $families. Pratique à risque probablement généralisée."
    }

    # Cas 3 : High présent, densité faible
    if ($highCount -gt 0) {
        $families = $Metrics.TopFamiliesCritHigh -join ' et '
        return "Aucun Critical, mais $highCount findings High isolés — majoritairement $families. Revue priorisée recommandée (score $score/100)."
    }

    # Cas 4 : pas de Critical/High, densité forte en Medium/Low
    if ($density -gt 30) {
        $families = $Metrics.TopFamiliesGlobal -join ' et '
        return "Aucun Critical/High. $density% des fichiers scannés contiennent au moins un finding — majoritairement $families. Volume élevé mais gravité faible, à nettoyer sans urgence."
    }

    # Cas 5 : cas sain
    return "Aucun Critical/High. Findings isolés ($density% des fichiers scannés) — environnement globalement maîtrisé (score $score/100)."
}

$scanMetrics = Get-ScanMetrics -ResultsRenamed $ResultsRenamed -TotalRisk $totalRisk -TotalFilesScanned $Objectfound

$verdictSummary = Get-RiskNarrative -Metrics $scanMetrics

#endregion Calcul potentiel risk

#endregion Summary

# Close all Slots and pool
$pool.Close()
$pool.Dispose()

#endregion Script
 
    Import-Module $htmlfunction -Force
    if ($CheckShares) {

    Write-Host "Check Share on Domain controllers" -ForegroundColor Cyan

    $SafeRights = @(
    'Read','ReadAndExecute',
    'Synchronize','ReadAttributes',
    'ReadExtendedAttributes','ReadPermissions',
    'AppendData','ReadData','ExecuteFile',
    '-1610612736','Synchronize'
    )

    $ShareonDC  = @()

    $listOfDCs = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().DomainControllers

    ForEach ($DC in $listOfDCs) {
    
    Write-Host "Check Shares on $DC" -ForegroundColor Cyan
    
    if (Test-Path "\\$DC\SYSVOL") {

    try {
        $Shares = Get-RemoteDiskShares -ComputerName $DC

        foreach ($share in $Shares) {

            $SharePath = "\\$DC\$($share.Name)"

            if (Test-Path $SharePath) {
                $Findings = Get-ShareACL -FilePath $SharePath -TrustGroups $Trustgroups -PermissionTrust $SafeRights

                if ($Findings) {
                    foreach ($finding in $Findings) {
                        $ShareonDC += [PSCustomObject]@{
                            DC        = $DC
                            Share     = $share.Name
                            Type      = $share.Type
                            ACL       = $finding.Pattern
                            Reason    = $finding.Reason
                            Comment   = $share.Comment
                        }
                    }
                } else {
                    $ShareonDC += [PSCustomObject]@{
                        DC        = $DC
                        Share     = $share.Name
                        Type      = $share.Type
                        ACL       = 'Clean'
                        Reason    = '-'
                        Comment   = $share.Comment
                    }
                }
            } 
            else {
                $ShareonDC += [PSCustomObject]@{
                    DC        = $DC
                    Share     = $share.Name
                    Type      = $share.Type
                    ACL       = 'Inaccessible'
                    Reason    = 'Test-Path failed'
                    Comment   = $share.Comment
                }
            }
        }

    } catch {
        Write-Warning "Erreur sur $DC : $_"
    }
    } else {
    $notAccessibleFiles.Add([PSCustomObject]@{
    Error = "SYSVOL is not accessible on $DC"
})

}
}
    }

#region HTML

    $tableId = 'ResultsTable'

    if ($Exportcsv) {

$Results | Export-Csv -Path $Exportcsv

} 
    elseif ($expocsv -eq 'coucou') {

    #Option Offline, without open nav for hosting on webserver
    if ($Silent.IsPresent) {
    $htmlvalue = $false } 
    else {
    $htmlvalue = $true  }    

$logo = "https://github.com/dakhama-mehdi/Harden-Sysvol/blob/main/Pictures/HardenSysvol.png?raw=true"
$rightlogo = "https://github.com/dakhama-mehdi/Harden-Sysvol/blob/main/Pictures/Rightlogo.png?raw=true"

$totalfiles = $runspaces.count
# Generate HTML report
Write-Host "[Step 4/4] Generate HTML ..." -ForegroundColor $MSGcolor
Write-Host "Resume : Founds $Objectfound in $totalfiles" -ForegroundColor Cyan

    #Option Offline, without open nav for hosting on webserver
    if ($Silent.IsPresent) {
    $htmlvalue = $false
    [string]$SavePath = $SavePath + "\hardensysvol.html" } 
    else {
    [string]$date = Get-Date -Format "yyMMdd_HHmm"
    [string]$SavePath = $SavePath + "\hardensysvol_$date.html"
    $htmlvalue = $true  }    

New-HTML -TitleText 'HardenSysvol' -FilePath $SavePath -ShowHTML:$htmlvalue {
    New-HTMLHeader  {
        New-HTMLSection -Invisible -Margin "0px 25px" { 
            New-HTMLPanel -Invisible -AlignContentText left {
                New-HTMLImage -Source $logo -Class 'otehr' -Width 30%  -Height 20%
            }           
            New-HTMLPanel -Invisible -AlignContentText right {
            New-HTMLText -LineBreak
            New-HTMLText -Text "Report date: $startDate" -Alignment right -FontSize 15
            }
        }
    }
    New-HTMLTab -Name '📊 Dashboard' {      
    New-HTMLTabStyle  -BackgroundColorActive '#004c87'
    New-HTMLSection -Invisible -Density Compact -Margin "0px 25px"  {
    New-HTMLInfoCard -Title "Domain"       -Number $dnsDomain  -Style Compact -Alignment Center -BackgroundColor Lavender
    New-HTMLInfoCard -Title "Scanned Files"  -Number $totalfiles   -Style Compact -Alignment Center -BackgroundColor Thistle
    New-HTMLInfoCard -Title "Suspicious Files"  -Number $Objectfound -Style Compact -Alignment Center -BackgroundColor Lavender
    New-HTMLInfoCard -Title "Elapsed Time"  -Number $elapsedTime -Style Compact -Alignment Center -BackgroundColor     Thistle
    }
    New-HTMLSection -name 'Summary' -Margin "0px 25px" -HeaderBackGroundColor '#004c87' {
    New-HTMLPanel  {
    New-HTMLGaugeLight -value $totalRisk -Title "Indicator Risk : $totalRisk"
    }
    New-HTMLPanel  {
    New-HTMLTableOption -DataStore HTML #-ArrayJoin -ArrayJoinString ',' -PrettifyObject
    New-HTMLTable -DataTable $top5path -HideFooter -HideButtons -HideShowButton -DisablePaging -DisableSearch  {
    if ($top5path) {
            New-TableHeader -Names 'Name', 'Count' -Title 'Top 5 Files' -Alignment center -BackGroundColor LightSteelBlue    
    }
        }
    }    
    New-HTMLPanel  {
    New-HTMLTable -DataTable $top5Words -HideFooter -HideButtons -HideShowButton -DisablePaging -DisableSearch {
    if ($top5Words) {
    New-TableHeader -Names 'name', 'count' -Title 'Top 5 Reason' -Alignment center -BackGroundColor LightSteelBlue
        }
    }
    }
    New-HTMLPanel  {
         New-HTMLList {
              New-HTMLListItem -Text 'Harden-Sysvol Version : 2.2.5' 
              New-HTMLListItem -Text 'Release : 01/2026' 
              New-HTMLListItem -Text 'Author : Dakhama Mehdi<br>
              <br> Credit : HardenAD Community [HardenAD](https://www.hardenad.net/)
              <br> Credit : It-connect Community [It-Connect](https://www.it-connect.fr/)
              <br> Thanks : Przemyslaw Klys [Evotec](https://evotec.xyz) for Module PSWriteHTML/PswriteOffice'

              } -FontSize 14
            }    

    }
    New-HTMLSection -Invisible -HeaderBackGroundColor '#004c87' -Margin "10px 25px" { 
    New-HTMLPanel  {
    New-HTMLTable -DataTable $groupedFiles -HideFooter -HideButtons -HideShowButton -PagingLength 5 -DisableSearch -DefaultSortColumn Count -DefaultSortOrder Descending {
    if ($groupedFiles) {
    New-HTMLTableHeader -Names 'name', 'count' -Title 'Types of Extensions found' -Alignment center -BackGroundColor '#004c87' -Color White
        }
    }
    }
    New-HTMLPanel  {
            New-HTMLImage -Source $rightlogo 
        }   
    } 
    }
    New-HTMLTab -Name '🔍 Details'   {   
    
    

    Add-HTMLStyle -Content @'
.card-wrapper { flex: 1 1 0; cursor: pointer; transition: opacity 0.2s ease-in-out, transform 0.1s ease-in-out, box-shadow 0.2s ease-in-out; border-radius: 6px; }
.card-wrapper:hover { transform: translateY(-2px); }
.card-wrapper.active { box-shadow: 0 0 0 2px #333; }
.summary-section:has(.card-wrapper.active) .card-wrapper:not(.active) { opacity: 0.45; }
'@


    New-HTMLSection {
        New-HTMLTag -Tag 'div' -Attributes @{ class = 'summary-section'; style = 'display:flex; gap:12px; width:100%; margin-bottom:10px;'; 'data-table-id' = $tableId } {
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'card-wrapper'; 'data-filter' = 'Critical' } {
                New-HTMLInfoCard -Title "Critical" -Number ($ResultsRenamed | Where-Object RiskLevel -eq 'Critical').Count -IconColor "#d32f2f" -Alignment Center -Style Compact -BorderRadius 0px -BackgroundColor '#f3f4f6'
            }
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'card-wrapper'; 'data-filter' = 'High' } {
                New-HTMLInfoCard -Title "High" -Number ($ResultsRenamed | Where-Object RiskLevel -eq 'High').Count -IconColor "#ff9800" -Alignment Center -Style Compact -BorderRadius 0px -BackgroundColor '#f3f4f6'
            }
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'card-wrapper'; 'data-filter' = 'Medium' } {
                New-HTMLInfoCard -Title "Medium" -Number ($ResultsRenamed | Where-Object RiskLevel -eq 'Medium').Count -IconColor "#fbc02d" -Alignment Center -Style Compact -BorderRadius 0px -BackgroundColor '#f3f4f6'
            }
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'card-wrapper'; 'data-filter' = 'Low' } {
                New-HTMLInfoCard -Title "Low" -Number ($ResultsRenamed | Where-Object RiskLevel -eq 'Low').Count -IconColor "#42a5f5" -Alignment Center -Style Compact -BorderRadius 0px -BackgroundColor '#f3f4f6'
            }
            New-HTMLTag -Tag 'div' -Attributes @{ class = 'card-wrapper'; 'data-filter' = 'Info' } {
                New-HTMLInfoCard -Title "Info" -Number ($ResultsRenamed | Where-Object RiskLevel -eq 'Info').Count -IconColor "#9e9e9e" -Alignment Center -Style Compact -BorderRadius 0px -BackgroundColor '#f3f4f6'
            }
        }
    }
    
    
    New-HTMLSection  -Margin "0px 25px" -Invisible -HeaderBackGroundColor '#004c87'  {
    New-HTMLTableOption -DataStore HTML -ArrayJoin -ArrayJoinString ',' -PrettifyObject
    New-HTMLTable -DataTable $ResultsRenamed -PagingLength 10 -DataTableID $tableId -HideShowButton -DefaultSortIndex 4 -DefaultSortOrder Descending -DisableAutoWidthOptimization -Buttons copyHtml5,csvHtml5,pdfHtml5,searchBuilder -HideFooter -Style cell-border {
        if ($ResultsRenamed) {
               #New-TableCondition -ColumnName 'Reason' -ComparisonType string -Operator 'eq' -Value 'WrongACL' -Color 'Red' -Row
               New-TableCondition -ColumnName 'RiskLevel' -ComparisonType string -Operator 'eq' -Value 'Critical' -BackgroundColor '#d32f2f' -Color White
               New-TableCondition -ColumnName 'RiskLevel' -ComparisonType string -Operator 'eq' -Value 'High' -BackgroundColor '#ef5350' -Color White
               New-TableCondition -ColumnName 'RiskLevel' -ComparisonType string -Operator 'eq' -Value 'Medium' -BackgroundColor '#ff9800' -Color White
               New-TableCondition -ColumnName 'RiskLevel' -ComparisonType string -Operator 'eq' -Value 'Low' -BackgroundColor '#ffa726' -Color Black  
               New-TableCondition -ColumnName 'RiskLevel' -ComparisonType string -Operator 'eq' -Value 'Info' -BackgroundColor '#ffa726' -Color Black      
               $headers = $ResultsRenamed[0].PSObject.Properties.Name
               #New-HTMLTableColumnOption -ColumnIndex 0 -Width 200px
               New-HTMLTableContent -WordBreak break-word -ColumnName 'FilePath'
               #New-HTMLTableColumnOption -ColumnIndex 2 -Width 300px
               New-HTMLTableContent -WordBreak break-word -ColumnName 'Value'
               New-TableColumnOption -ColumnIndex 4 -Hidden:$true
               #New-TableRowGrouping -ColumnID 3 -Color black -BackgroundColor LightBlue
               foreach ($head in $headers) {
               New-HTMLTableHeader -Names $head -Color White -BackGroundColor '#004c87'
               }
        }
        }
        Add-HTMLScript -Content @'
document.addEventListener('DOMContentLoaded', function() {
    var section = document.querySelector('.summary-section[data-table-id="ResultsTable"]');
    if (!section) return;
    section.addEventListener('click', function(e) {
        var card = e.target.closest('.card-wrapper');
        if (!card) return;
        var table = document.getElementById('ResultsTable');
        if (!table) return;
        var dt = (window.jQuery && jQuery.fn.DataTable) ? jQuery(table).DataTable() : null;
        if (!dt) return;
 
        card.classList.toggle('active');
 
        var activeFilters = [];
        section.querySelectorAll('.card-wrapper.active').forEach(function(c) {
            var f = c.getAttribute('data-filter');
            if (f) { activeFilters.push(f); }
        });
 
        if (activeFilters.length === 0) {
            dt.column(3).search('').draw();
        } else {
            var pattern = activeFilters.map(function(f) {
                return '^' + f.replace(/[-\[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + '$';
            }).join('|');
            dt.column(3).search(pattern, true, false).draw();
        }
    });
});
'@

    }

    
    if ($CheckShares) {
    New-HTMLSection -Invisible -Margin "10px 25px"  {
    New-HTMLTableOption -DataStore HTML
    New-htmlTable -DataTable $ShareonDC -TextWhenNoData 'No Share suspect found' -HideFooter -HideButtons -HideShowButton -DisableSearch {
        if ($ShareonDC) {
        New-TableHeader -Names 'DC', 'Share','Type','ACL','Reason','Comment' -Title 'Suspicious Shares' -Alignment center -BackGroundColor DarkGreen -Color White
    }
    }
        }
    }
    New-HTMLSection -Invisible -Margin "10px 25px" {
    New-HTMLTableOption -DataStore HTML -PrettifyObject
    New-htmlTable -DataTable $notAccessibleFiles -TextWhenNoData 'No errors during scanning' -HideFooter -HideButtons -HideShowButton -DisablePaging -DisableSearch {
        if ($notAccessibleFiles) {
        New-TableHeader -Names 'Error' -Title 'Errors logs' -Alignment center -BackGroundColor Coral -Color White
    }
    }
        }
     } 
}
}
    else {

    $About = "Harden-Sysvol `n Version : 3.0.0 `n Author : Dakhama Mehdi `n`n Credit : Harden Community [Harden](https://www.hardenad.net/) `n Credit : It-connect Community [It-Connect](https://www.it-connect.fr/)`nThanks : Przemyslaw Klys [Evotec](https://evotec.xyz) for Module PSWriteHTML/PswriteOffice `n Copyright : 2026"

    Import-Module ModernHTML -Force

    $totalfiles = $runspaces.count

    New-ModernHTML -HeaderColor Sky -OutputPath "C:\Temp\HardenSysvol_Report.html" -Content {

        New-Header -Left { "HardenSysvol" } -Right { Get-HeaderText -Text "Report Date : $startDate" }

        New-SectionTop {
           Get-ScorePanel -Title 'Global risk score' -Subtitle 'Static test score - For entry' -Score $totalRisk
           Get-TextPanel -Text $verdictSummary
           Get-StatPanel -Items @(
            "Domain;$dnsDomain;Green"
            "Scanned Files;$totalfiles;gold"
            "Suspicious Files;$Objectfound;Red"
            "Elapsed Time;$elapsedTime"
            )
        }

        New-SectionSideNav {
           
           
        New-tab -Title 'Dashboard' {
          New-SectionCard {
          Get-RiskCard -Label 'Critical' -Value ($riskCounts | Where-Object Name -eq 'Critical').Count -Color Red
          Get-RiskCard -Label 'High'     -Value ($riskCounts | Where-Object Name -eq 'High').Count     -Color Orange
          Get-RiskCard -Label 'Medium'   -Value ($riskCounts | Where-Object Name -eq 'Medium').Count   -Color Gold
          Get-RiskCard -Label 'Low'      -Value ($riskCounts | Where-Object Name -eq 'Low').Count      -Color Sky
          Get-RiskCard -Label 'Info'     -Value ($riskCounts | Where-Object Name -eq 'Info').Count     -Color Slate
        }
          
           New-SectionTop {
            Get-TopPanel -Title 'Principaux motifs' -Items $top5Words
            Get-TopPanel -Title 'Extensions les plus fréquentes' -Items $top5extensions -Color Green
            Get-TopPanel -Title 'Top 5 Files' -Items $top5path -Color Red
        }

}

        New-Tab -Title 'Details' {
           New-SectionTable {

            Get-HtmlTable -SortColumn 4 -SortOrder 'desc' -HiddenColumns 4 -Title 'Résultats du scan' -Responsive -Rows $ResultsRenamed -ColorRules @{ 
            'RiskLevel' = @{
            'Critical' = 'Red'
            'Medium'   = 'Gold'
            }
        }
                    }
        }

        New-tab -Title 'logs' {
           New-SectionTable {
                    if ($CheckShares) {
            Get-HtmlTable -Title 'Share Scan' -Rows $ShareonDC
            }
  
            Get-HtmlTable -Title 'Errors' -Rows $notAccessibleFiles
            }
            }

        New-tab -Title 'About' {
            Get-TextPanel -FontSize 15 -Text $about
        }


}

    }

#endregion HTML

}
}
# SIG # Begin signature block
# MIItjQYJKoZIhvcNAQcCoIItfjCCLXoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBdYhBE12OsEZoZ
# Zwdl+dS4KD5Adbcu2wXOjQCZ/IsTZaCCEtUwggXJMIIEsaADAgECAhAbtY8lKt8j
# AEkoya49fu0nMA0GCSqGSIb3DQEBDAUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
# ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy
# dGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5l
# dHdvcmsgQ0EwHhcNMjEwNTMxMDY0MzA2WhcNMjkwOTE3MDY0MzA2WjCBgDELMAkG
# A1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAl
# BgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMb
# Q2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMIICIjANBgkqhkiG9w0BAQEFAAOC
# Ag8AMIICCgKCAgEAvfl4+ObVgAxknYYblmRnPyI6HnUBfe/7XGeMycxca6mR5rlC
# 5SBLm9qbe7mZXdmbgEvXhEArJ9PoujC7Pgkap0mV7ytAJMKXx6fumyXvqAoAl4Va
# qp3cKcniNQfrcE1K1sGzVrihQTib0fsxf4/gX+GxPw+OFklg1waNGPmqJhCrKtPQ
# 0WeNG0a+RzDVLnLRxWPa52N5RH5LYySJhi40PylMUosqp8DikSiJucBb+R3Z5yet
# /5oCl8HGUJKbAiy9qbk0WQq/hEr/3/6zn+vZnuCYI+yma3cWKtvMrTscpIfcRnNe
# GWJoRVfkkIJCu0LW8GHgwaM9ZqNd9BjuiMmNF0UpmTJ1AjHuKSbIawLmtWJFfzcV
# WiNoidQ+3k4nsPBADLxNF8tNorMe0AZa3faTz1d1mfX6hhpneLO/lv403L3nUlbl
# s+V1e9dBkQXcXWnjlQ1DufyDljmVe2yAWk8TcsbXfSl6RLpSpCrVQUYJIP4ioLZb
# MI28iQzV13D4h1L92u+sUS4Hs07+0AnacO+Y+lbmbdu1V0vc5SwlFcieLnhO+Nqc
# noYsylfzGuXIkosagpZ6w7xQEmnYDlpGizrrJvojybawgb5CAKT41v4wLsfSRvbl
# jnX98sy50IdbzAYQYLuDNbdeZ95H7JlI8aShFf6tjGKOOVVPORa5sWOd/7cCAwEA
# AaOCAT4wggE6MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLahVDkCw6A/joq8
# +tT4HKbROg79MB8GA1UdIwQYMBaAFAh2zcsH/yT2xc3tu5C84oQ3RnX3MA4GA1Ud
# DwEB/wQEAwIBBjAvBgNVHR8EKDAmMCSgIqAghh5odHRwOi8vY3JsLmNlcnR1bS5w
# bC9jdG5jYS5jcmwwawYIKwYBBQUHAQEEXzBdMCgGCCsGAQUFBzABhhxodHRwOi8v
# c3ViY2Eub2NzcC1jZXJ0dW0uY29tMDEGCCsGAQUFBzAChiVodHRwOi8vcmVwb3Np
# dG9yeS5jZXJ0dW0ucGwvY3RuY2EuY2VyMDkGA1UdIAQyMDAwLgYEVR0gADAmMCQG
# CCsGAQUFBwIBFhhodHRwOi8vd3d3LmNlcnR1bS5wbC9DUFMwDQYJKoZIhvcNAQEM
# BQADggEBAFHCoVgWIhCL/IYx1MIy01z4S6Ivaj5N+KsIHu3V6PrnCA3st8YeDrJ1
# BXqxC/rXdGoABh+kzqrya33YEcARCNQOTWHFOqj6seHjmOriY/1B9ZN9DbxdkjuR
# mmW60F9MvkyNaAMQFtXx0ASKhTP5N+dbLiZpQjy6zbzUeulNndrnQ/tjUoCFBMQl
# lVXwfqefAcVbKPjgzoZwpic7Ofs4LphTZSJ1Ldf23SIikZbr3WjtP6MZl9M7JYjs
# NhI9qX7OAo0FmpKnJ25FspxihjcNpDOO16hO0EoXQ0zF8ads0h5YbBRRfopUofbv
# n3l6XYGaFpAP4bvxSgD5+d2+7arszgowggZHMIIEL6ADAgECAhA12OBytW+cTayv
# VHUpRhwLMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhB
# c3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNp
# Z25pbmcgMjAyMSBDQTAeFw0yNTExMTYxMTAwMTlaFw0yNjExMTYxMTAwMThaMG0x
# CzAJBgNVBAYTAkZSMQ8wDQYDVQQHDAZUb3Vsb24xHjAcBgNVBAoMFU9wZW4gU291
# cmNlIERldmVsb3BlcjEtMCsGA1UEAwwkT3BlbiBTb3VyY2UgRGV2ZWxvcGVyLCBE
# QUtIQU1BIE1FSERJMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAp6Ku
# m/VmkWCqAaF/3zHh9f1FuJYY2ozbXOu7mo1/Q8i1c0fE0TXpkZXLY2GZbfpj9BmH
# AAFM0IhOsPR2vdxq3jOUJUb9TICneFor6YaPpySsXR3WSE7X42kgpkkmPELovm1Y
# hwSzhJ4a+E+NWL/MU8h5JpmGVlqPJ02/ZTlMj5kcpIQtq8hoQMcUEDkGFt9IcamE
# 1yN4IHkBA5nm4jJPaos0IuS77t805992JSGWhxBxWARH+2vyltv8Rmq1pZV1lE6n
# JgrWT7Ichjw2X/A+OP68ooTzQwCIpzXb4UuUcwHEfrmP3HGMQJoj//SNC4QPMao+
# 3Z8zbevl73E3d6Kfvra1S+pWM2Ze5YCsIqAd98GUHgi5E6GiG8FQq/+d6msL7l8B
# UASCqXlcAKIjRNMHp8BrUaaW6HS9Kpc+3O3t/LUmK6X3FFiW8QsWoh4K+7YSpopa
# CQbNXmEI4xftctwBOJrEU2oqRnYiwchfjqBNlrGwVGPK1rmM0iTt5KiLTus7AgMB
# AAGjggF4MIIBdDAMBgNVHRMBAf8EAjAAMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6
# Ly9jY3NjYTIwMjEuY3JsLmNlcnR1bS5wbC9jY3NjYTIwMjEuY3JsMHMGCCsGAQUF
# BwEBBGcwZTAsBggrBgEFBQcwAYYgaHR0cDovL2Njc2NhMjAyMS5vY3NwLWNlcnR1
# bS5jb20wNQYIKwYBBQUHMAKGKWh0dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9j
# Y3NjYTIwMjEuY2VyMB8GA1UdIwQYMBaAFN10XUwA23ufoHTKsW73PMAywHDNMB0G
# A1UdDgQWBBSXTmfHi9BD9GDRwk5/doNtKHBXYzBLBgNVHSAERDBCMAgGBmeBDAEE
# ATA2BgsqhGgBhvZ3AgUBBDAnMCUGCCsGAQUFBwIBFhlodHRwczovL3d3dy5jZXJ0
# dW0ucGwvQ1BTMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDAN
# BgkqhkiG9w0BAQsFAAOCAgEAe+khGqwUUkFYuFRsrvenX2/a+PIt2Tu9d3VoW6Or
# MX3YLpe7S2CgFkXwEi2Siq5KiD1labP9jsh/3G1ZQwwlnPv8dB7ocl/nOrQ9OZex
# GVE1r7IO6VYVa5F7XuJ/KadKLEbQSs1BpBVhESo1ZYr6w9NCLuO9q2Sh3H5MktET
# D6sB+g1TFOYMdwYl8eAawgI2kGPe3dRQSoumP0mHkm3x5SIwRCW+08md5uyzCIui
# 85WmcNPtM1QCqjkSpfdFGYPsnf/BO9NATpZkqFxhXwa9+PqseX+mofCIL49guCXG
# kU4RpeRHcUie14oYkxvBw7VUO4MT6wYbS2C3j2nyoAV4XqqNMfrhZIBJG5haj2RB
# V46bMJ+DsW6hxlm3lIlCaJT2pLbbk79OP+Bk0HIdC9mAbKzcqaZpBpn4+ljrcx7/
# X7OHv4XTCCDWwlZbaogy4Wci6TiSjjfpfXK5N/eJTEEh2w4qoYTTrR61ptkVnTUT
# vGRfPnVtS/3aOm2v4UahtOc/ygcL0A/J85r1e6CEeOaTm9eJbHoNdwNIYaZ81VlX
# /V/MoJgFCtioYOKiTf2Rdq7XrEEHLU2YGwCqJyKYz9tz10yXBcMW6/+gX+PGqAYz
# eKg5jbKLdi9lVrKspQUXAPHdcl6VJMXy799J0lbsQeJNgBVy6HWxOWvdLBGX3hPE
# 3aYwgga5MIIEoaADAgECAhEAmaOACiZVO2Wr3G6EprPqOTANBgkqhkiG9w0BAQwF
# ADCBgDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVz
# IFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEk
# MCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMB4XDTIxMDUxOTA1
# MzIxOFoXDTM2MDUxODA1MzIxOFowVjELMAkGA1UEBhMCUEwxITAfBgNVBAoTGEFz
# c2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIGA1UEAxMbQ2VydHVtIENvZGUgU2ln
# bmluZyAyMDIxIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnSPP
# BDAjO8FGLOczcz5jXXp1ur5cTbq96y34vuTmflN4mSAfgLKTvggv24/rWiVGzGxT
# 9YEASVMw1Aj8ewTS4IndU8s7VS5+djSoMcbvIKck6+hI1shsylP4JyLvmxwLHtSw
# orV9wmjhNd627h27a8RdrT1PH9ud0IF+njvMk2xqbNTIPsnWtw3E7DmDoUmDQiYi
# /ucJ42fcHqBkbbxYDB7SYOouu9Tj1yHIohzuC8KNqfcYf7Z4/iZgkBJ+UFNDcc6z
# okZ2uJIxWgPWXMEmhu1gMXgv8aGUsRdaCtVD2bSlbfsq7BiqljjaCun+RJgTgFRC
# tsuAEw0pG9+FA+yQN9n/kZtMLK+Wo837Q4QOZgYqVWQ4x6cM7/G0yswg1ElLlJj6
# NYKLw9EcBXE7TF3HybZtYvj9lDV2nT8mFSkcSkAExzd4prHwYjUXTeZIlVXqj+ea
# YqoMTpMrfh5MCAOIG5knN4Q/JHuurfTI5XDYO962WZayx7ACFf5ydJpoEowSP07Y
# aBiQ8nXpDkNrUA9g7qf/rCkKbWpQ5boufUnq1UiYPIAHlezf4muJqxqIns/kqld6
# JVX8cixbd6PzkDpwZo4SlADaCi2JSplKShBSND36E/ENVv8urPS0yOnpG4tIoBGx
# VCARPCg1BnyMJ4rBJAcOSnAWd18Jx5n858JSqPECAwEAAaOCAVUwggFRMA8GA1Ud
# EwEB/wQFMAMBAf8wHQYDVR0OBBYEFN10XUwA23ufoHTKsW73PMAywHDNMB8GA1Ud
# IwQYMBaAFLahVDkCw6A/joq8+tT4HKbROg79MA4GA1UdDwEB/wQEAwIBBjATBgNV
# HSUEDDAKBggrBgEFBQcDAzAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vY3JsLmNl
# cnR1bS5wbC9jdG5jYTIuY3JsMGwGCCsGAQUFBwEBBGAwXjAoBggrBgEFBQcwAYYc
# aHR0cDovL3N1YmNhLm9jc3AtY2VydHVtLmNvbTAyBggrBgEFBQcwAoYmaHR0cDov
# L3JlcG9zaXRvcnkuY2VydHVtLnBsL2N0bmNhMi5jZXIwOQYDVR0gBDIwMDAuBgRV
# HSAAMCYwJAYIKwYBBQUHAgEWGGh0dHA6Ly93d3cuY2VydHVtLnBsL0NQUzANBgkq
# hkiG9w0BAQwFAAOCAgEAdYhYD+WPUCiaU58Q7EP89DttyZqGYn2XRDhJkL6P+/T0
# IPZyxfxiXumYlARMgwRzLRUStJl490L94C9LGF3vjzzH8Jq3iR74BRlkO18J3zId
# mCKQa5LyZ48IfICJTZVJeChDUyuQy6rGDxLUUAsO0eqeLNhLVsgw6/zOfImNlARK
# n1FP7o0fTbj8ipNGxHBIutiRsWrhWM2f8pXdd3x2mbJCKKtl2s42g9KUJHEIiLni
# 9ByoqIUul4GblLQigO0ugh7bWRLDm0CdY9rNLqyA3ahe8WlxVWkxyrQLjH8ItI17
# RdySaYayX3PhRSC4Am1/7mATwZWwSD+B7eMcZNhpn8zJ+6MTyE6YoEBSRVrs0zFF
# IHUR08Wk0ikSf+lIe5Iv6RY3/bFAEloMU+vUBfSouCReZwSLo8WdrDlPXtR0gicD
# nytO7eZ5827NS2x7gCBibESYkOh1/w1tVxTpV2Na3PR7nxYVlPu1JPoRZCbH86gc
# 96UTvuWiOruWmyOEMLOGGniR+x+zPF/2DaGgK2W1eEJfo2qyrBNPvF7wuAyQfiFX
# LwvWHamoYtPZo0LHuH8X3n9C+xN4YaNjt2ywzOr+tKyEVAotnyU9vyEVOaIYMk3I
# eBrmFnn0gbKeTTyYeEEUz/Qwt4HOUBCrW602NCmvO1nm+/80nLy5r0AZvCQxaQ4x
# ghoOMIIaCgIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0
# YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNpZ25pbmcgMjAy
# MSBDQQIQNdjgcrVvnE2sr1R1KUYcCzANBglghkgBZQMEAgEFAKB8MBAGCisGAQQB
# gjcCAQwxAjAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC
# AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCCxXJbdY8cF95IG+kl2
# jOghrcxhyvQicnZOsvkbP60amjANBgkqhkiG9w0BAQEFAASCAYBL2wE4thUdvH66
# WFHPaye23dZ00LNY91GtlvmlfDwSU+D5yKvfRQUkS8K7kyZBdNcM5ReMhBi3rdLl
# 1fYuSessFrOO8HT46iLXHp/KPMBy/kXLmNVjssmcdDWiQyJBvYrSiTDf/MbvHWPJ
# fF84pLn3kg13sF8DwEZZIe1mVdOBqU8WrXXlckMSEkUFVZ/UD2gGGAgqCY0hgpzm
# s4qqb4fIyP2j3nFmOdLGvhsuiXuS380+yWy3e5fnyOCebMX6MhUwr371FN7hoi2l
# rk04/+twKsR3PbUvcmXjUAuGbU3Av+xo6RJrYA+lO6tPRmHZ1IZo+8kloDuT7C4A
# gCocxVAcLlbi4A1sHDpVEbdXfkncg47pC2x83ntsGcsA4zfMzVpTjuKsJ8lwRhcX
# Zqi2hEnTFK5wy9CeurPYhw2MjDISWC4BykwA8KUoWEmQTBZUncspMkH6uUDtUi2T
# dfEQlDFqytOTRxDDw7Sk6oklsoMp3w8v1xSb3Yx8qs5QvdlUx9yhghd3MIIXcwYK
# KwYBBAGCNwMDATGCF2MwghdfBgkqhkiG9w0BBwKgghdQMIIXTAIBAzEPMA0GCWCG
# SAFlAwQCAQUAMHgGCyqGSIb3DQEJEAEEoGkEZzBlAgEBBglghkgBhv1sBwEwMTAN
# BglghkgBZQMEAgEFAAQghT0Rj7/K8F1LfFeFT8qIda9k3rmguXQ13cmx7wxuMiEC
# EQCjv4IaaVl0QrWGYm+BHlegGA8yMDI2MDkyOTIxMDQxMVqgghM6MIIG7TCCBNWg
# AwIBAgIQCE/cM09+RU7bww+P+ZIYNTANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQG
# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0
# IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0Ex
# MB4XDTI2MDgwNTAwMDAwMFoXDTM3MTEwNDIzNTk1OVowYzELMAkGA1UEBhMCVVMx
# FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBTSEEy
# NTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVyIDIwMjYgMTCCAiIwDQYJKoZI
# hvcNAQEBBQADggIPADCCAgoCggIBALZ7pvLJ/s1K+NSbTGWz/TjGMPh8CQ6RucZC
# Lv5anHzWJjF/NWJrFIhy24fcpKXlgRiky4WAawDfU3YP0BMxt9l3Dm5oCG5Z69Aq
# EN1kgHg2epx+l+lZBcmJCcN0ASURML5uFIS80sZsDwO3BSkUxDjLJhBI+qiZP3ai
# xAC/qEGLjsBNlLol9VZ7pfGEXiMlneJIC5/YKuizVzNFKZZEeoy/0B8Zm+nzKBgS
# WG52lCO1w+nCg6XpCtklTJXeIg283hw7TmmsZXR+SMbjbrEOvZ3fP2VxIgeR28Y9
# 0ZStd3F9VuA5RVynb/whITPAo9b75Zr4Ta6Mj3URm26QZYMn/FnbuTegcoRcFEZ9
# FOqM5T6MTdtr/n74lIT/ug0eeOzmZ6QTFg33otX+bFRsIolvykE1jive4PuESaT8
# zzVeFWDAMDtozNgLctkGD1ZjkEyZtJrLl5ya0m5doH/ScpaZCZVl6pNUOCybMc/k
# xC6EAmSJY24L0yYKD1Nkddsnb/ItVKi/2nXpQNMu1PT5prW83vV8d67WowuUs0Hd
# Y4H8AMLGvdL/WHEj3ZnqMqAQQP9u3Ai9t+5eQ02GDwy0ODjdzi0xlp70W+ow63/0
# ++YDEX1M0iwgUHwbrJvfpklkZQvw3+kv3vUPItdwroczk9icflf55W1zOEKAcJVA
# IXpcMCU9AgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQUyWOK
# MC7USvtulPPm40B+9ezN4jAfBgNVHSMEGDAWgBTvb1NK6eQGfHrK4pBW9i/USezL
# TjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwgZUGCCsG
# AQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j
# b20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp
# Q2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNy
# dDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln
# aUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5j
# cmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEB
# CwUAA4ICAQCNxTphHp1SCt+ZrAmAfn0oQLFr0mLywSLaDXQIENoyKqxrFbJblzCV
# P/pkXmwXOdrOpWygLzlT12os5ipDCy35RBCg2UMeApEtrfGhz45F4Wt4WGdNdIbR
# Wt3YTYJmpR+b7lr4d7Uwn+H600u4D7RnOGf8Wj4UNgAdZkfHhHv1mx9EVh71SJel
# cEN/oORSjXzdjfw1iZH9d8Nh/thn6hH23d+VsPAr6GAYyzSA02nXD1nYLI7Ijmiv
# +xLCiYC41DSFYL3GhTiy0PxpawPtGRyaBVGzq+UiTfM8pD7KVyF5aQyWP4KhVGUU
# Tnmm/RlYJoW3TiXA/+t0YcT2oRVBm3JETjajHug2AL+v5jhtKVnd3D0rbHXEu27o
# +Q8p4sEWPMqKDB+qbceb6T/6WcwTwXmQ9lOCLLYcsQeSWmvKqzpAec9etE14jOQA
# zLKWdE3w/TCaKtLRaRT7LCkRYVnhA2D73FLje1O5b3HR5eHs0NzU/+xX7NbEdcof
# y0W3Wdwd1XOqtlpg/JgwtKfZM5dqO94lbUveOiJBI+xZEbGRsMNbXmMREUTgu+Oc
# a7Y73MPWcslIx2VhkSKSXjDbD6rgg39H5Mh7QfieAIjWagkJNt68Yfim6cjEzVSi
# LSeZfdkr5dtFPTW6jATlWJdYeeDRGCyatf8R1hSjzSvdN8yWQPT9gzCCBrQwggSc
# oAMCAQICEA3HrFcF/yGZLkBDIgw6SYYwDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE
# BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj
# ZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTI1
# MDUwNzAwMDAwMFoXDTM4MDExNDIzNTk1OVowaTELMAkGA1UEBhMCVVMxFzAVBgNV
# BAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0
# IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMTCCAiIwDQYJKoZI
# hvcNAQEBBQADggIPADCCAgoCggIBALR4MdMKmEFyvjxGwBysddujRmh0tFEXnU2t
# jQ2UtZmWgyxU7UNqEY81FzJsQqr5G7A6c+Gh/qm8Xi4aPCOo2N8S9SLrC6Kbltqn
# 7SWCWgzbNfiR+2fkHUiljNOqnIVD/gG3SYDEAd4dg2dDGpeZGKe+42DFUF0mR/vt
# La4+gKPsYfwEu7EEbkC9+0F2w4QJLVSTEG8yAR2CQWIM1iI5PHg62IVwxKSpO0Xa
# F9DPfNBKS7Zazch8NF5vp7eaZ2CVNxpqumzTCNSOxm+SAWSuIr21Qomb+zzQWKhx
# KTVVgtmUPAW35xUUFREmDrMxSNlr/NsJyUXzdtFUUt4aS4CEeIY8y9IaaGBpPNXK
# FifinT7zL2gdFpBP9qh8SdLnEut/GcalNeJQ55IuwnKCgs+nrpuQNfVmUB5KlCX3
# ZA4x5HHKS+rqBvKWxdCyQEEGcbLe1b8Aw4wJkhU1JrPsFfxW1gaou30yZ46t4Y9F
# 20HHfIY4/6vHespYMQmUiote8ladjS/nJ0+k6MvqzfpzPDOy5y6gqztiT96Fv/9b
# H7mQyogxG9QEPHrPV6/7umw052AkyiLA6tQbZl1KhBtTasySkuJDpsZGKdlsjg4u
# 70EwgWbVRSX1Wd4+zoFpp4Ra+MlKM2baoD6x0VR4RjSpWM8o5a6D8bpfm4CLKczs
# G7ZrIGNTAgMBAAGjggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
# BBTvb1NK6eQGfHrK4pBW9i/USezLTjAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/
# 57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYI
# KwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j
# b20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp
# Q2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9j
# cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1Ud
# IAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEA
# F877FoAc/gc9EXZxML2+C8i1NKZ/zdCHxYgaMH9Pw5tcBnPw6O6FTGNpoV2V4wzS
# UGvI9NAzaoQk97frPBtIj+ZLzdp+yXdhOP4hCFATuNT+ReOPK0mCefSG+tXqGpYZ
# 3essBS3q8nL2UwM+NMvEuBd/2vmdYxDCvwzJv2sRUoKEfJ+nN57mQfQXwcAEGCvR
# R2qKtntujB71WPYAgwPyWLKu6RnaID/B0ba2H3LUiwDRAXx1Neq9ydOal95CHfmT
# nM4I+ZI2rVQfjXQA1WSjjf4J2a7jLzWGNqNX+DF0SQzHU0pTi4dBwp9nEC8EAqox
# W6q17r0z0noDjs6+BFo+z7bKSBwZXTRNivYuve3L2oiKNqetRHdqfMTCW/NmKLJ9
# M+MtucVGyOxiDf06VXxyKkOirv6o02OoXN4bFzK0vlNMsvhlqgF2puE6FndlENSm
# E+9JGYxOGLS/D284NHNboDGcmWXfwXRy4kbu4QFhOm0xJuF2EZAOk5eCkhSxZON3
# rGlHqhpB/8MluDezooIs8CVnrpHMiD2wL40mm53+/j7tFaxYKIqL0Q4ssd8xHZnI
# n/7GELH3IdvG2XlM9q7WP/UwgOkw/HQtyRN62JK4S1C8uw3PdBunvAZapsiI5YKd
# vlarEvf8EA+8hcpSM9LHJmyrxaFtoza2zNaQ9k+5t1wwggWNMIIEdaADAgECAhAO
# mxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUw
# EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
# JDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEw
# MDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE
# aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMT
# GERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
# ADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprN
# rnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVy
# r2iTcMKyunWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4
# IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13j
# rclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4Q
# kXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQn
# vKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu
# 5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/
# 8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQp
# JYls5Q5SUUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFf
# xCBRa2+xq4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGj
# ggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/
# 57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8B
# Af8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
# cC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6
# oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElE
# Um9vdENBLmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEB
# AHCgv0NcVec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0a
# FPQTSnovLbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNE
# m0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZq
# aVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCs
# WKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9Fc
# rBjDTZ9ztwGpn1eqXijiuZQxggN8MIIDeAIBATB9MGkxCzAJBgNVBAYTAlVTMRcw
# FQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3Rl
# ZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTECEAhP3DNP
# fkVO28MPj/mSGDUwDQYJYIZIAWUDBAIBBQCggdEwGgYJKoZIhvcNAQkDMQ0GCyqG
# SIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjA5MjkyMTA0MTFaMCsGCyqGSIb3
# DQEJEAIMMRwwGjAYMBYEFFHZq9oDSXPYT0JmrKSCSOazacQ5MC8GCSqGSIb3DQEJ
# BDEiBCAbZNmW2nbqvVTZE00IPqSZ6oGqmmVGOxbfkGXM/KcXXDA3BgsqhkiG9w0B
# CRACLzEoMCYwJDAiBCAtoJ2n9BMfn+cttsXm6cllZ1WvBD8ep0LMDSEg4UHr/DAN
# BgkqhkiG9w0BAQEFAASCAgBVq2LvAJrnJDZakNRLPROSYgncMZUTtLZZ7GSbRAZs
# m7qHdZctpZV+BPmubfc5r84cKEGLnUCwoine2VtUDHPrGsXXtHPscoG2MOA7QsLo
# MDxi5jutiJM0SJ3d8GcZ++78XF0KRGMinvzBbOGM5N0Im8poyM4JQo/7DYA7m5tl
# xASFg4gHIj6IizsGo3mInjK8cQMPq+AcchrlNWKGWWqkLivXlA04/xXrZeTmu5fN
# LKAIc+i8WI1fr1IRgm7R06hKsvt85deJ7JuOW04LGjlT13SqHZodFVmjlI1z4fJC
# ytEsnIHtTOlwNeh8+boEg3ftGAkpmBfp1HmxtoitI0DNciiKPaZjaWxVW/gzI7RC
# +MR76ZhDwa7trPFqFSKSpxzGONRyB4Pm9/m0InwteR3HTUtgagsiL6l0fa3Y87df
# 0CZtcfmWXoo4XcWpc5/vax0dYYT7aM4qR9ytlrmfgFsU+lcJvwPrOCzwgIXMgt3j
# VFLEbH5l3P0BL+kex4y9EisgT6a7KiLBd+/Mr/+hmdegYPKUV7kVMmEDozpB6UFs
# KiPz2J7i1DkfLc4rFWe3P9diAb71hyEGwVMQegRd0jPehP5sMi2WfNSTHaZmiIzQ
# Ugvzo8xpUctYc6hz3zl4o53mlYPEYURS7m3ABaboBLe3TnB1NVwaq9uGVRxp5QgM
# uw==
# SIG # End signature block