Modules/Support/ScubaBaselineSchemaHelper.psm1

<#
# Example usage:
[string]$ResourceRoot = ($PWD.ProviderPath, $PSScriptRoot)[[bool]$PSScriptRoot]
 
# Get the markdown mappings
$markdownMappings = Get-ScubaConfigExclusionMappingsFromMarkdown -BaselineDirectory "..\..\..\baselines"
 
# Update configuration using markdown mappings from GitHub
Update-ScubaConfigBaselineWithMarkdown -BaselineFilePath ".\ScubaBaselines_en-US.json" -GitHubDirectoryUrl "https://github.com/cisagov/ScubaGear/tree/main/PowerShell/ScubaGear/baselines"
 
# Update configuration using local markdown files
Update-ScubaConfigBaselineWithMarkdown -BaselineFilePath ".\ScubaBaselines_en-US.tests.json" -BaselineDirectory "..\..\..\baselines"
 
# Filter specific products -
Update-ScubaConfigBaselineWithMarkdown -BaselineFilePath ".\ScubaBaselines_en-US.tests.json" -GitHubDirectoryUrl "https://github.com/cisagov/ScubaGear/tree/main/PowerShell/ScubaGear/baselines" -ProductFilter @("aad", "defender", "exo")
 
# Update configuration with additional fields
Update-ScubaConfigBaselineWithMarkdown -BaselineFilePath ".\ScubaBaselines_en-US.tests.json" -GitHubDirectoryUrl "https://github.com/cisagov/ScubaGear/tree/main/PowerShell/ScubaGear/baselines" -AdditionalFields @('criticality')
#>


function Get-ScubaConfigExclusionMappingsFromMarkdown {
    <#
    .SYNOPSIS
    Extracts exclusion type mappings from hidden markers in baseline markdown files.
 
    .DESCRIPTION
    This function parses baseline markdown files to extract exclusion type mappings using
    hidden markers in the format <!--ExclusionType: TypeName-->. This approach is simpler
    and more maintainable than parsing Rego files.
 
    .PARAMETER BaselineDirectory
    The directory containing baseline markdown files. Defaults to the standard ScubaGear baselines directory.
 
    .PARAMETER GitHubDirectoryUrl
    The URL of the GitHub directory containing baseline policy files.
 
    .EXAMPLE
    $mappings = Get-ScubaConfigExclusionMappingsFromMarkdown -BaselineDirectory "C:\Path\To\ScubaGear\baselines"
    #>

    param(
        [Parameter(Mandatory=$false)]
        [string]$BaselineDirectory,

        [Parameter(Mandatory=$false)]
        [string]$GitHubDirectoryUrl
    )

    $exclusionMappings = @{}
    $files = @()

    # Get markdown files from GitHub or local directory
    if ($GitHubDirectoryUrl) {
        # Convert GitHub URL to API URL
        if ($GitHubDirectoryUrl -match '^https://github.com/([^/]+)/([^/]+)/tree/([^/]+)(?:/(.*))?$') {
            $owner = $matches[1]
            $repo = $matches[2]
            $branch = $matches[3]
            $path = if ($matches[4]) { $matches[4] } else { "" }
            $apiUrl = "https://api.github.com/repos/$owner/$repo/contents/$path`?ref=$branch"
        } else {
            throw "Invalid GitHub URL format. Expected format: https://github.com/owner/repo/tree/branch/path"
        }

        # Get list of markdown files in the directory
        $response = Invoke-RestMethod -Uri $apiUrl
        $files = $response | Where-Object { $_.name -like "*.md" }
    } elseif ($BaselineDirectory) {
        $files = Get-ChildItem -Path $BaselineDirectory -Filter *.md
    } else {
        throw "You must provide either -BaselineDirectory or -GitHubDirectoryUrl."
    }

    # Process each markdown file
    foreach ($file in $files) {
        if ($GitHubDirectoryUrl) {
            $content = (Invoke-WebRequest -Uri $file.download_url).Content
            $lines = $content -split "`n"
        } else {
            $lines = Get-Content -Path $file.FullName -Encoding UTF8
        }

        # Normalize problematic Unicode characters to standard ASCII equivalents
        for ($j = 0; $j -lt $lines.Count; $j++) {
            $lines[$j] = $lines[$j] -replace [char]0x2019, "'"    # Right single quotation mark to apostrophe
            $lines[$j] = $lines[$j] -replace [char]0x201C, '"'    # Left double quotation mark
            $lines[$j] = $lines[$j] -replace [char]0x201D, '"'    # Right double quotation mark
            $lines[$j] = $lines[$j] -replace [char]0x2013, '-'    # En dash to hyphen
            $lines[$j] = $lines[$j] -replace [char]0x2014, '--'   # Em dash to double hyphen
            $lines[$j] = $lines[$j] -replace '’', "'"           # Fix UTF-8 encoding error
            $lines[$j] = $lines[$j] -replace '“', '"'           # Fix UTF-8 encoding error
            $lines[$j] = $lines[$j] -replace 'â€\x9d', '"'         # Fix UTF-8 encoding error
        }

        $currentPolicyId = $null

        for ($i = 0; $i -lt $lines.Count; $i++) {
            $line = $lines[$i]

            # Look for policy ID headers (#### MS.XXX.#.#v#)
            if ($line -match '^####\s+(MS\.[A-Z]+\.[0-9]+\.[0-9]+v[0-9]+)\s*$') {
                $currentPolicyId = $matches[1]
            }

            # Look for hidden exclusion type marker
            if ($currentPolicyId -and $line -match '<!--\s*ExclusionType:\s*(\w+)\s*-->') {
                $exclusionType = $matches[1]
                if (-not $exclusionMappings.ContainsKey($currentPolicyId)) {
                    $exclusionMappings[$currentPolicyId] = $exclusionType
                    Write-Verbose "Found mapping: $currentPolicyId -> $exclusionType (from $($file.Name))"
                }
            }
        }
    }

    return $exclusionMappings
}

function Update-ScubaConfigBaselineWithMarkdown {
    <#
    .SYNOPSIS
    Updates the baseline configuration using exclusion mappings extracted from markdown file markers.
 
    .DESCRIPTION
    This function uses hidden markers in the baseline markdown files to determine the exclusion types
    used by each policy, providing a simpler approach than parsing Rego files. Also handles version
    increment, debug mode reset, and adds required fields to all baselines.
 
    .PARAMETER BaselineFilePath
    The path to the configuration file that will be updated.
 
    .PARAMETER BaselineDirectory
    The local directory containing baseline policy files.
 
    .PARAMETER GitHubDirectoryUrl
    The URL of the GitHub directory containing baseline policy files.
 
    .PARAMETER ProductFilter
    An array of product names to filter the policies.
 
    .PARAMETER AdditionalFields
    An array of additional fields to include in the policy objects. Available fields: criticality, lastModified, implementation, mitreMapping, resources, link, badges.
 
    .EXAMPLE
    Update-ScubaConfigBaselineWithMarkdown -BaselineFilePath ".\ScubaBaselines_en-US.json" -GitHubDirectoryUrl "https://github.com/cisagov/ScubaGear/tree/main/PowerShell/ScubaGear/baselines"
    #>

    param(
        [Parameter(Mandatory=$false)]
        [string]$BaselineFilePath = "$env:Temp\ScubaBaselines.json",

        [Parameter(Mandatory=$false)]
        [string]$BaselineDirectory,

        [Parameter(Mandatory=$false)]
        [string]$GitHubDirectoryUrl,

        [Parameter(Mandatory=$false)]
        [string[]]$ProductFilter = @(),

        [Parameter(Mandatory=$false)]
        [string[]]$AdditionalFields = @("criticality", "lastModified", "implementation", "mitreMapping", "resources", "licenseRequirements", "link", "badges")
    )

    # Get the exclusion mappings from markdown file markers
    Write-Output "Analyzing markdown files for exclusion mappings..."
    #if Paramter is BaselineDirectory use that, else use GitHubDirectoryUrl
    if ($BaselineDirectory) {
        $markdownMappings = Get-ScubaConfigExclusionMappingsFromMarkdown -BaselineDirectory $BaselineDirectory
    } else {
        $markdownMappings = Get-ScubaConfigExclusionMappingsFromMarkdown -GitHubDirectoryUrl $GitHubDirectoryUrl
    }

    Write-Output "Found $($markdownMappings.Keys.Count) policy exclusion mappings from markdown files"

    # Extract common header content from the first baseline markdown file
    Write-Output "Extracting common header content from baseline markdown files..."
    if ($BaselineDirectory) {
        $headerContent = Get-ScubaBaselineHeaderContent -BaselineDirectory $BaselineDirectory
    } else {
        $headerContent = Get-ScubaBaselineHeaderContent -GitHubDirectoryUrl $GitHubDirectoryUrl
    }

    # Create new configuration structure everytime!
    $configContent = [PSCustomObject]@{
        Version = "1.0.0"
        DebugMode = "None"
        baselines = @{}
    }

    # Add header content if extracted successfully
    if ($headerContent) {
        $configContent | Add-Member -NotePropertyName "Introduction" -NotePropertyValue $headerContent.Introduction
        $configContent | Add-Member -NotePropertyName "LicenseCompliance" -NotePropertyValue $headerContent.LicenseCompliance
        $configContent | Add-Member -NotePropertyName "Assumptions" -NotePropertyValue $headerContent.Assumptions
        $configContent | Add-Member -NotePropertyName "KeyTerminology" -NotePropertyValue $headerContent.KeyTerminology
        Write-Output "Added common header content to configuration"
    }

    Write-Output "Creating new configuration file: $BaselineFilePath"


    # 1. Update version using current date (year offset from 2025, month, day)
    if ($configContent.Version) {
        $currentVersion = $configContent.Version
        $currentDate = Get-Date

        # Calculate year offset from 2025 (UI development start year)
        $uiStartYear = 2025
        $currentYear = $currentDate.Year
        $yearOffset = $currentYear - $uiStartYear + 1  # +1 so 2025 = 1, 2026 = 2, etc.

        $month = $currentDate.Month       # Current month (1-12)
        $day = $currentDate.Day           # Current day (1-31)
        $dateString = Get-Date -Format "M/d/yyyy"

        $newVersion = "$yearOffset.$month.$day"
        $configContent.Version = "$newVersion [updated $dateString]"
        Write-Output "Updated version from '$currentVersion' to '$($configContent.Version)'"
    }

    # Get baseline policies
    $baselinePolicies = Get-ScubaBaselinePolicy -BaselineDirectory $BaselineDirectory -GitHubDirectoryUrl $GitHubDirectoryUrl

    # Filter products if specified
    if ($ProductFilter.Count -gt 0) {
        $filteredPolicies = @{}
        foreach ($product in $ProductFilter) {
            if ($baselinePolicies.ContainsKey($product)) {
                $filteredPolicies[$product] = $baselinePolicies[$product]
            }
        }
        $baselinePolicies = $filteredPolicies
    }

    # Create new baselines structure with alphabetical ordering
    $newBaselines = [ordered]@{}
    $mappingStats = @{}

    # Sort products alphabetically for consistent output
    $sortedProducts = $baselinePolicies.Keys | Sort-Object

    foreach ($product in $sortedProducts) {
        $policies = $baselinePolicies[$product]
        $productBaseline = @()

        foreach ($policy in $policies) {
            # Use markdown mapping if available, otherwise default to "none"
            $exclusionField = if ($markdownMappings.ContainsKey($policy.PolicyId)) {
                $markdownMappings[$policy.PolicyId]
            } else {
                "none"
            }

            # Track mapping statistics
            if (-not $mappingStats.ContainsKey($exclusionField)) {
                $mappingStats[$exclusionField] = 0
            }
            $mappingStats[$exclusionField]++

            # 3. Create policy object with required fields including omissionField and annotationField
            $policyObj = [ordered]@{
                id = $policy.PolicyId
                name = $policy.Title
                policySection = $policy.PolicySection
                sectionDescription = $policy.SectionDescription
                exclusionField = $exclusionField
                omissionField = "Omissions"
                annotationField = "Annotations"
            }

            # Add rationale if available (always try to include this)
            if ($policy.Rationale) {
                $policyObj['rationale'] = $policy.Rationale
            }

            # Add additional fields if specified and they exist
            foreach ($field in $AdditionalFields) {
                switch ($field) {
                    "criticality" {
                        if ($policy.Criticality) {
                            $policyObj['criticality'] = $policy.Criticality
                        }
                    }
                    "lastModified" {
                        if ($policy.LastModified) {
                            $policyObj['lastModified'] = $policy.LastModified
                        }
                    }
                    "implementation" {
                        if ($policy.Implementation) {
                            $policyObj['implementation'] = $policy.Implementation
                        }
                    }
                    "mitreMapping" {
                        if ($policy.MITRE_Mapping -and $policy.MITRE_Mapping.Count -gt 0) {
                            $policyObj['mitreMapping'] = $policy.MITRE_Mapping
                        }
                    }
                    "resources" {
                        if ($policy.SectionResources -and $policy.SectionResources.Count -gt 0) {
                            $policyObj['resources'] = $policy.SectionResources
                        }
                    }
                    "licenseRequirements" {
                        if ($policy.SectionLicenseRequirements -and $policy.SectionLicenseRequirements.Count -gt 0) {
                            # Ensure licenseRequirements is always an array
                            if ($policy.SectionLicenseRequirements -is [array]) {
                                $policyObj['licenseRequirements'] = $policy.SectionLicenseRequirements
                            } else {
                                $policyObj['licenseRequirements'] = @($policy.SectionLicenseRequirements)
                            }
                        } else {
                            # Default to "N/A" as a single-item array for consistency
                            $policyObj['licenseRequirements'] = @("N/A")
                        }
                    }
                    "link" {
                        if ($policy.Link) {
                            $policyObj['link'] = $policy.Link
                        }
                    }
                    "badges" {
                        if ($policy.Badges -and $policy.Badges.Count -gt 0) {
                            $policyObj['badges'] = $policy.Badges
                        }
                    }
                }
            }

            $productBaseline += $policyObj
        }

        if ($productBaseline.Count -gt 0) {
            $newBaselines[$product] = $productBaseline
        }
    }

    # Update the configuration
    $configContent.baselines = $newBaselines

    # Save the updated configuration with proper character encoding
    $jsonOutput = $configContent | ConvertTo-Json -Depth 10

    # Fix common UTF-8 encoding issues in the JSON output using character codes
    $jsonOutput = $jsonOutput -replace ([char]0x2019), "'"        # Right single quotation mark
    $jsonOutput = $jsonOutput -replace ([char]0x201C), '"'        # Left double quotation mark
    $jsonOutput = $jsonOutput -replace ([char]0x201D), '"'        # Right double quotation mark
    $jsonOutput = $jsonOutput -replace 'â€"', "—"                 # Fix malformed double dash
    $jsonOutput = $jsonOutput -replace '’', "'"                 # Fix malformed apostrophe
    $jsonOutput = $jsonOutput -replace '“', '"'                 # Fix malformed left quote
    $jsonOutput = $jsonOutput -replace 'â€', '"'                  # Fix malformed right quote

    $jsonOutput | Set-Content $BaselineFilePath -Encoding UTF8

    Write-Output "Successfully updated baselines in configuration file: $BaselineFilePath"
    Write-Output "Updated products: $($newBaselines.Keys -join ', ')"

    # Show exclusion type statistics
    Write-Output "`nExclusion Type Statistics:"
    foreach ($exclusionField in ($mappingStats.Keys | Sort-Object)) {
        Write-Output " $exclusionField`: $($mappingStats[$exclusionField]) policies"
    }

    # Detailed summary
    foreach ($product in $newBaselines.Keys) {
        $policies = $newBaselines[$product]
        $policyCount = $policies.Count
        $exclusionCounts = $policies | Group-Object exclusionField | ForEach-Object { "$($_.Name): $($_.Count)" }
        Write-Output " $product`: $policyCount policies ($($exclusionCounts -join ', '))"
    }

    return $newBaselines
}

function Get-ScubaBaselineHeaderContent {
    <#
    .SYNOPSIS
    Extracts the common header content from the first baseline markdown file.
 
    .DESCRIPTION
    Extracts content from the introduction section, specifically the last 3 paragraphs
    that start with "The Secure Cloud Business Applications (SCuBA) project...",
    "The CISA SCuBA SCBs...", and "For non-Federal users...".
    Also extracts License Compliance, Assumptions, and Key Terminology sections.
 
    .PARAMETER BaselineDirectory
    The local directory containing baseline policy files.
 
    .PARAMETER GitHubDirectoryUrl
    The URL of the GitHub directory containing baseline policy files.
    #>

    param(
        [Parameter(Mandatory=$false)]
        [string]$BaselineDirectory,

        [Parameter(Mandatory=$false)]
        [string]$GitHubDirectoryUrl
    )

    $files = @()

    if ($GitHubDirectoryUrl) {
        # Convert GitHub URL to API URL
        if ($GitHubDirectoryUrl -match '^https://github.com/([^/]+)/([^/]+)/tree/([^/]+)(?:/(.*))?$') {
            $owner = $matches[1]
            $repo = $matches[2]
            $branch = $matches[3]
            $path = if ($matches[4]) { $matches[4] } else { "" }
            $apiUrl = "https://api.github.com/repos/$owner/$repo/contents/$path`?ref=$branch"
        } else {
            throw "Invalid GitHub URL format. Expected format: https://github.com/owner/repo/tree/branch/path"
        }

        # Get list of markdown files in the directory
        $response = Invoke-RestMethod -Uri $apiUrl
        $files = $response | Where-Object { $_.name -like "*.md" }
    } elseif ($BaselineDirectory) {
        $files = Get-ChildItem -Path $BaselineDirectory -Filter *.md
    } else {
        throw "You must provide either -BaselineDirectory or -GitHubDirectoryUrl."
    }

    # Use the first markdown file (e.g., aad.md)
    if ($files.Count -eq 0) {
        return $null
    }

    $firstFile = $files[0]
    $content = ""

    if ($GitHubDirectoryUrl) {
        $content = (Invoke-WebRequest -Uri $firstFile.download_url).Content
    } else {
        $content = Get-Content -Path $firstFile.FullName -Raw -Encoding UTF8
    }

    # Normalize problematic Unicode characters in the header content
    $content = $content -replace [char]0x2019, "'"       # Right single quotation mark to apostrophe
    $content = $content -replace [char]0x201C, '\"'       # Left double quotation mark
    $content = $content -replace [char]0x201D, '\"'       # Right double quotation mark
    $content = $content -replace [char]0x2013, '-'       # En dash to hyphen
    $content = $content -replace [char]0x2014, '--'      # Em dash to double hyphen
    $content = $content -replace '’', "'"              # Fix UTF-8 encoding error
    $content = $content -replace '“', '\"'              # Fix UTF-8 encoding error
    $content = $content -replace 'â€\x9d', '\"'            # Fix UTF-8 encoding error

    $headerContent = @{
        introduction = ""
        licenseCompliance = ""
        assumptions = ""
        keyTerminology = ""
    }

    # Extract introduction content - get only the common SCuBA project description
    if ($content -match '(?s)The Secure Cloud Business Applications \(SCuBA\) project.*?(?=## License Compliance and Copyright)') {
        $introSection = $matches[0].Trim()
        $headerContent.introduction = $introSection
    }

    # Extract License Compliance and Copyright section
    if ($content -match '(?s)## License Compliance and Copyright\s*\n(.*?)(?=\n## |$)') {
        $headerContent.licenseCompliance = $matches[1].Trim()
    }

    # Extract Assumptions section
    if ($content -match '(?s)## Assumptions\s*\n(.*?)(?=\n## |$)') {
        $headerContent.assumptions = $matches[1].Trim()
    }

    # Extract Key Terminology section
    if ($content -match '(?s)## Key Terminology\s*\n(.*?)(?=\n## |$)') {
        $headerContent.keyTerminology = $matches[1].Trim()
    }

    return $headerContent
}


function Get-ScubaBaselinePolicy {
    <#
    .SYNOPSIS
    Retrieves the baseline policy for a specific product using hierarchical section parsing.
 
    .PARAMETER BaselineDirectory
    Specifies the directory containing baseline policy files.
 
    .PARAMETER GitHubDirectoryUrl
    Specifies the GitHub directory URL containing baseline policy files.
 
    .EXAMPLE
     Get-ScubaBaselinePolicy -BaselineDirectory $BaselineDirectory -GitHubDirectoryUrl $GitHubDirectoryUrl
    #>

    param(
        [Parameter(Mandatory=$false)]
        [string]$BaselineDirectory,

        [Parameter(Mandatory=$false)]
        [string]$GitHubDirectoryUrl
    )

    $policiesByProduct = @{}
    $files = @()

    if ($GitHubDirectoryUrl) {
        # Convert GitHub URL to API URL
        if ($GitHubDirectoryUrl -match '^https://github.com/([^/]+)/([^/]+)/tree/([^/]+)(?:/(.*))?$') {
            $owner = $matches[1]
            $repo = $matches[2]
            $branch = $matches[3]
            $path = if ($matches[4]) { $matches[4] } else { "" }
            $apiUrl = "https://api.github.com/repos/$owner/$repo/contents/$path`?ref=$branch"
        } else {
            throw "Invalid GitHub URL format. Expected format: https://github.com/owner/repo/tree/branch/path"
        }

        # Get list of markdown files in the directory
        $response = Invoke-RestMethod -Uri $apiUrl
        $files = $response | Where-Object { $_.name -like "*.md" }
    } elseif ($BaselineDirectory) {
        $files = Get-ChildItem -Path $BaselineDirectory -Filter *.md
    } else {
        throw "You must provide either -BaselineDirectory or -GitHubDirectoryUrl."
    }

    foreach ($file in $files) {
        if ($GitHubDirectoryUrl) {
            $content = (Invoke-WebRequest -Uri $file.download_url).Content
            $lines = $content -split "`n"
        } else {
            $lines = Get-Content -Path $file.FullName -Encoding UTF8
        }

        # Normalize problematic Unicode characters to standard ASCII equivalents
        for ($j = 0; $j -lt $lines.Count; $j++) {
            $lines[$j] = $lines[$j] -replace [char]0x2019, "'"    # Right single quotation mark to apostrophe
            $lines[$j] = $lines[$j] -replace [char]0x201C, '"'    # Left double quotation mark
            $lines[$j] = $lines[$j] -replace [char]0x201D, '"'    # Right double quotation mark
            $lines[$j] = $lines[$j] -replace [char]0x2013, '-'    # En dash to hyphen
            $lines[$j] = $lines[$j] -replace [char]0x2014, '--'   # Em dash to double hyphen
            $lines[$j] = $lines[$j] -replace '’', "'"           # Fix UTF-8 encoding error
            $lines[$j] = $lines[$j] -replace '“', '"'           # Fix UTF-8 encoding error
            $lines[$j] = $lines[$j] -replace 'â€\x9d', '"'         # Fix UTF-8 encoding error
        }

        # Parse hierarchically by sections
        $fullContent = $lines -join "`n"
        $sections = Get-ScubaBaselineSections -Content $fullContent

        $policies = @()

        foreach ($section in $sections) {
            foreach ($policy in $section.Policies) {
                # Add section context to each policy
                $policy.PolicySection = $section.Name
                $policy.SectionDescription = $section.Description
                $policy.SectionResources = $section.Resources
                $policy.SectionLicenseRequirements = $section.LicenseRequirements
                $policies += $policy
            }
        }

        if ($policies.Count -gt 0) {
            $productName = ($file.Name -replace '\.md$', '').ToLower()
            $policiesByProduct[$productName] = $policies
        }
    }

    return $policiesByProduct
}

function Get-ScubaBaselineSections {
    <#
    .SYNOPSIS
    Parses baseline markdown content hierarchically by sections (## level headers)
 
    .PARAMETER Content
    The full markdown content to parse
    #>

    param([string]$Content)

    $sections = @()
    $lines = $Content -split "`n"

    $currentSection = $null
    $currentSubSection = $null
    $currentPolicy = $null
    $currentContent = @()

    for ($i = 0; $i -lt $lines.Count; $i++) {
        $line = $lines[$i]

        # ## Section headers (e.g., "## 1. Legacy Authentication")
        if ($line -match '^##\s+\d+\.\s*(.+)$') {
            # Save previous section
            if ($currentSection) {
                $sections += $currentSection
            }

            # Start new section
            $currentSection = @{
                Name = $matches[1].Trim()
                Description = ""
                Resources = @()
                LicenseRequirements = @()
                Policies = [System.Collections.ArrayList]::new()
            }
            $currentSubSection = $null
            $currentPolicy = $null
            $currentContent = @()
            continue
        }

        # ### Sub-section headers (Policies, Resources, License Requirements, Implementation)
        if ($line -match '^###\s+(.+)$') {
            $subSectionName = $matches[1].Trim()

            # Save current policy if we were in one
            if ($currentPolicy -and $currentSubSection -eq "Policies" -and $currentSection) {
                $currentPolicy.Content = ($currentContent -join "`n").Trim()
                $policyDetails = Get-ScubaPolicyContent -Content $currentPolicy.Content
                $currentPolicy.Criticality = $policyDetails.Criticality
                $currentPolicy.LastModified = $policyDetails.LastModified
                $currentPolicy.Rationale = $policyDetails.Rationale
                $currentPolicy.MITRE_Mapping = $policyDetails.MITRE_Mapping
                $currentPolicy.Badges = $policyDetails.Badges
                # Extract implementation instructions for this policy
                $currentPolicy.Implementation = Get-ScubaPolicyImplementation -Content $Content -PolicyId $currentPolicy.PolicyId
                $null = $currentSection.Policies.Add($currentPolicy)
                $currentPolicy = $null
                $currentContent = @()
            }

            # Process section-level content based on subsection type
            if ($currentSubSection -eq "Resources") {
                $currentSection.Resources = Get-ScubaSectionContent -Content ($currentContent -join "`n") -ContentType "Resources"
            } elseif ($currentSubSection -eq "License Requirements") {
                $sectionLicenseReq = Get-ScubaSectionContent -Content ($currentContent -join "`n") -ContentType "LicenseRequirements"
                $currentSection.LicenseRequirements = if ($sectionLicenseReq -and $sectionLicenseReq.Count -gt 0) { $sectionLicenseReq } else { @("N/A") }
            }

            $currentSubSection = $subSectionName
            $currentContent = @()
            continue
        }

        # #### Policy headers (e.g., "#### MS.AAD.1.1v1")
        if ($line -match '^####\s+(MS\.[A-Z]+\.[0-9]+\.[0-9]+v[0-9]+)\s*$') {
            # Save previous policy
            if ($currentPolicy -and $currentSection) {
                $currentPolicy.Content = ($currentContent -join "`n").Trim()
                $policyDetails = Get-ScubaPolicyContent -Content $currentPolicy.Content
                $currentPolicy.Criticality = $policyDetails.Criticality
                $currentPolicy.LastModified = $policyDetails.LastModified
                $currentPolicy.Rationale = $policyDetails.Rationale
                $currentPolicy.MITRE_Mapping = $policyDetails.MITRE_Mapping
                $currentPolicy.Badges = $policyDetails.Badges
                # Extract implementation instructions for this policy
                $currentPolicy.Implementation = Get-ScubaPolicyImplementation -Content $Content -PolicyId $currentPolicy.PolicyId
                $null = $currentSection.Policies.Add($currentPolicy)
                $currentContent = @()
            }

            # Start new policy
            $currentPolicy = @{
                PolicyId = $matches[1]
                Title = ""
                Content = ""
            }
            continue
        }

        # Policy title (first non-empty line after policy header)
        if ($currentPolicy -and $currentSubSection -eq "Policies" -and -not $currentPolicy.Title -and $line.Trim() -ne '') {
            $currentPolicy.Title = $line.Trim()
            continue
        }

        # Description content (between ## section header and first ### subsection)
        if ($currentSection -and -not $currentSubSection -and $line.Trim() -ne '') {
            if ($currentSection.Description -eq "") {
                $currentSection.Description = $line.Trim()
            } else {
                $currentSection.Description += " " + $line.Trim()
            }
            continue
        }

        # Collect content for current context
        $currentContent += $line
    }

    # Save final policy and section
    if ($currentPolicy -and $currentSection) {
        $currentPolicy.Content = ($currentContent -join "`n").Trim()
        $policyDetails = Get-ScubaPolicyContent -Content $currentPolicy.Content
        $currentPolicy.Criticality = $policyDetails.Criticality
        $currentPolicy.LastModified = $policyDetails.LastModified
        $currentPolicy.Rationale = $policyDetails.Rationale
        $currentPolicy.MITRE_Mapping = $policyDetails.MITRE_Mapping
        $currentPolicy.Badges = $policyDetails.Badges
        $currentPolicy.Implementation = Get-ScubaPolicyImplementation -Content $Content -PolicyId $currentPolicy.PolicyId
        $null = $currentSection.Policies.Add($currentPolicy)
    }

    # Process final section content
    if ($currentSection) {
        if ($currentSubSection -eq "Resources") {
            $currentSection.Resources = Get-ScubaSectionContent -Content ($currentContent -join "`n") -ContentType "Resources"
        } elseif ($currentSubSection -eq "License Requirements") {
            $sectionLicenseReq = Get-ScubaSectionContent -Content ($currentContent -join "`n") -ContentType "LicenseRequirements"
            $currentSection.LicenseRequirements = if ($sectionLicenseReq -and $sectionLicenseReq.Count -gt 0) { $sectionLicenseReq } else { @("N/A") }
        }
        $sections += $currentSection
    }

    return $sections
}

function Get-ScubaSectionContent {
    <#
    .SYNOPSIS
    Parses section content for Resources or License Requirements
    #>

    param(
        [string]$Content,
        [string]$ContentType
    )

    $result = @()

    if ($ContentType -eq "Resources") {
        # Parse links for Resources
        $linkPattern = '\[([^\]]+)\]\(([^)]+)\)'
        $linkMatches = [regex]::Matches($Content, $linkPattern)

        foreach ($match in $linkMatches) {
            $linkName = $match.Groups[1].Value.Trim()
            $linkUrl = $match.Groups[2].Value.Trim()
            $result += @{ Name = $linkName; Url = $linkUrl }
        }
    }
    elseif ($ContentType -eq "LicenseRequirements") {
        # Parse bullet points for License Requirements - only split on actual bullets
        # Use regex to find lines that start with bullet markers (- at beginning of line, possibly with whitespace)
        $lines = $Content -split "`r?`n"
        $currentBullet = ""

        foreach ($line in $lines) {
            $line = $line.Trim()
            if ($line -match '^-\s*(.*)$') {
                # This is a new bullet point - save previous one if it exists
                if (-not [string]::IsNullOrWhiteSpace($currentBullet)) {
                    $result += $currentBullet.Trim()
                }
                # Start new bullet with the content after the dash
                $currentBullet = $matches[1].Trim()
            } elseif (-not [string]::IsNullOrWhiteSpace($line) -and -not [string]::IsNullOrWhiteSpace($currentBullet)) {
                # This is a continuation of the current bullet point
                $currentBullet += " " + $line
            }
        }

        # Don't forget the last bullet point
        if (-not [string]::IsNullOrWhiteSpace($currentBullet)) {
            $result += $currentBullet.Trim()
        }

        # If no bullets found, ensure we still return an array (even if empty)
        if ($result.Count -eq 0) {
            $result = @()
        }
    }

    return $result
}

function Get-ScubaPolicyImplementation {
    <#
    .SYNOPSIS
    Extracts implementation instructions for a specific policy
    #>

    param(
        [string]$Content,
        [string]$PolicyId
    )

    # Look for implementation section for this policy
    $pattern = "(?ms)^####\s+$PolicyId\s+Instructions\s*\n(.*?)(?=^####|^###|^##|\z)"

    if ($Content -match $pattern) {
        $implementation = $matches[1].Trim()

        # Normalize smart quotes to straight quotes
        $implementation = $implementation -replace [char]0x201C, '"'  # Left double quotation mark
        $implementation = $implementation -replace [char]0x201D, '"'  # Right double quotation mark
        $implementation = $implementation -replace [char]0x2019, "'"  # Right single quotation mark

        # Clean up markdown code blocks
        # First, handle indented code blocks (4+ spaces or 1+ tabs followed by backticks)
        $implementation = $implementation -replace '(?ms)^[ \t]*```[\w]*\r?\n(.*?)\r?\n[ \t]*```[ \t]*$', '$1'

        # Handle regular code blocks at start of line
        $implementation = $implementation -replace '(?ms)^```[\w]*\r?\n(.*?)\r?\n```[ \t]*$', '$1'

        # Handle inline code blocks and remaining artifacts
        $implementation = $implementation -replace '```[\w]*\r?\n?', ''
        $implementation = $implementation -replace '\r?\n?```', ''
        $implementation = $implementation -replace '```', ''

        # Clean up extra whitespace that might remain
        $implementation = $implementation -replace '^\s+', ''
        $implementation = $implementation -replace '\s+$', ''

        return $implementation
    }

    return ""
}

function Get-ScubaPolicyContent {
    <#
    .SYNOPSIS
        Retrieves the content of a specific policy from the markdown documentation
 
    .PARAMETER Content
        Import markdown content
    #>

    param([string]$Content)
    $result = [ordered]@{
        Criticality = $null
        LastModified = $null
        Rationale = $null
        MITRE_Mapping = @()
        Badges = @()
        LicenseRequirements = @()
    }
    if ($Content -match '<!--Policy:\s*[^;]+;\s*Criticality:\s*([A-Z]+)\s*-->') {
        $result.Criticality = $matches[1]
    }
    if ($Content -match '- _Last modified:_\s*(.+)') {
        $result.LastModified = $matches[1].Trim()
    }
    if ($Content -match '(?s)- _Rationale:_\s*(.+?)(?=\n\s*-|\n\s*\n|\z)') {
        $rationale = $matches[1].Trim()
        $result.Rationale = $rationale -replace '\s+', ' '
    }
    if ($Content -match '(_MITRE ATT&CK TTP Mapping:_[\s\S]+?)(\n\s*\n|###|$)') {
        $mitreBlock = $matches[1]
        $mitreList = @()
        foreach ($line in $mitreBlock -split "`n") {
            if ($line -match '\[([^\]]+)\]\(([^)]+)\)') {
                $mitreList += [ordered]@{ Name = $matches[1]; Url = $matches[2] }
            }
        }
        $result.MITRE_Mapping = $mitreList
    }

    # Parse policy-level license requirements
    if ($Content -match '(?ms)>\s*### License Requirements\s*\n+(?<Block>.*?)(?=\n\s*###|\n\s*\n\s*####|\z)') {
        $licenseBlock = $matches['Block']
        $cleanedBlock = $licenseBlock -replace "`n", " " -replace '\s+', ' '
        $bulletItems = $cleanedBlock -split '-' | Where-Object { $_.Trim() -ne '' }

        foreach ($item in $bulletItems) {
            $item = $item.Trim()
            # Filter out section headers and empty items, but keep N/A
            if ($item -and -not [string]::IsNullOrWhiteSpace($item) -and $item -notmatch '^###\s+') {
                $result.LicenseRequirements += $item
            }
        }
    }

    # Parse badges from shield.io patterns - handle both full URLs and anchor links
    $badgePattern = '\[!\[(?<Title>[^\]]+)\]\((?<ImageUrl>https?://img\.shields\.io/badge/[^)]+)\)\](?:\((?<LinkUrl>[^\)]+)\))?'
    $badgeMatches = [regex]::Matches($Content, $badgePattern)
    $badges = @()
    foreach ($match in $badgeMatches) {
        # Extract color and label from the image URL
        $imageUrl = $match.Groups['ImageUrl'].Value
        $label = ""
        $color = ""

        # Parse badge URL to extract label and color - handle various patterns including double dashes
        if ($imageUrl -match 'https?://img\.shields\.io/badge/(.+)-([A-Fa-f0-9]{3,6})$') {
            $fullLabelPart = $matches[1] -replace '%20', ' '  # Replace URL encoded spaces
            $color = $matches[2]
            # Extract just the readable label (remove underscores and technical formatting)
            $label = $fullLabelPart -replace '_', ' ' -replace '--', '-'
        }

        $linkUrl = $match.Groups['LinkUrl'].Value
        if (-not $linkUrl) { $linkUrl = "" }  # Handle badges without links

        $badges += [ordered]@{
            label = $label
            linkUrl = $linkUrl
            color = $color
        }
    }
    $result.Badges = $badges

    return $result
}



# Export module members
Export-ModuleMember -Function Get-ScubaBaselinePolicy, Get-ScubaConfigExclusionMappingsFromMarkdown, Update-ScubaConfigBaselineWithMarkdown, Get-ScubaBaselineSections, Get-ScubaBaselineHeaderContent

# SIG # Begin signature block
# MIIu9wYJKoZIhvcNAQcCoIIu6DCCLuQCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCLNtfKrsTCuEyc
# 0ItXRBTaC3Uh3lFbmwCPArj1dzelkaCCE6MwggWQMIIDeKADAgECAhAFmxtXno4h
# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
# eE4wggdXMIIFP6ADAgECAhAMM6tnPejLgA9WVhXroQvSMA0GCSqGSIb3DQEBCwUA
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwHhcNMjYwMTE0MDAwMDAwWhcNMjcwMTEzMjM1OTU5WjBfMQsw
# CQYDVQQGEwJVUzEdMBsGA1UECBMURGlzdHJpY3Qgb2YgQ29sdW1iaWExEzARBgNV
# BAcTCldhc2hpbmd0b24xDTALBgNVBAoTBENJU0ExDTALBgNVBAMTBENJU0EwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCuXYolNHqlh6smLTE592waXheZ
# 8VHzxeds4pMaepGuwmjf8d1jG9wUNuJX9/qb0a1dgGz5D/EAz5NRTIin4SZYQEE8
# qvdl2yQJ5uWxXIjsFbrOyc1fWscUXw0Kt7OPLOafcEkdDoe8K0tO4h2GL3RWRzjp
# uLfQhhnAmD6NT1l+ughnfmarV/ODgIn/RFR4YORlu4YP2xQX6KRxeTDslg7F+z6X
# +t87/U8m8gQ9XTm5kBmteP4GcE/ytnyI+ScIxNRybzGomWIBm848XDE5yYhlYQ2R
# SnCoo6M4CRqp9WFGVyoLkoPP0OlxzryKWaE1/nuPbYG/kf/rUB1OhqxvSSGwmNhs
# vkkjsC0Z9H5Jy6heFdoxOu/+ZQksKoP/fMvHxuCCtkIJbV8tk0oT6MQ8EJbgsWDZ
# TKhui1wxW6JIZyBOMPWoZUOouOzo2h5Cz7LBPKME5FkcUzcs47lpRlDkJco4PLcj
# wJSo4XPnx3G/2DIjNEFNyfKCWfH8uW6nJjmDBiveFZ2j0YvgdQ+7MOjQnw7R/MAD
# DTagrKl3rLV60+X2TY6/onKhCUuU3pMAjVbOwZ3PkzDLZnsEGRfm6hgp6014aXml
# t8h4nu+uC41U8vUSGHl0vqKuzvmShLmnI+Iv0l95pmnqomuCZzRDrjEoaLPx7OxL
# Dy/Id9E7yQDip4jBdQIDAQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8R
# hvv+YXsIiGX0TkIwHQYDVR0OBBYEFN30sVU+fpfQQfPQWMhkO1qd+MxoMD4GA1Ud
# IAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNl
# cnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMw
# gbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j
# cmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0
# ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMIGUBggrBgEF
# BQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t
# MFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl
# cnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJ
# BgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQA77T42YiOx5wWPItgo+kvB+Gzb
# ZFCRHRFfTAZZNQt9o/0CqNmyA2xFklX4t5Z9VNdiIOx14AnmJdQkcRdk3vsU5gby
# jKEup7LTWtvcWrl6hQwGNt3l892BgUbPKsPBE+AriktVqn5yMSXVVzeboqsqAG8e
# Syei0B54/QdgR4whfHvQ/qpCACsJTlJgAykXVgDPJNKnQ7wc17loLQutqF1JUcbO
# XKWt1AA9Zas5q30LDZzZeK4B56yojK68CTQXN7toSRFIuZDMKKDfIZpCX8cmbaaO
# DFOOu44/QWMv+Xc6+ISYGkrTTzqWhOqiXgLVBeXGn/WrOJJ8R29mZMneCpBesCLs
# YII1gCFOo7Vt6mvOKxAPQ3KhJYBFEHkp+GI65koaQkO2xv50iLS0+/j2YC66uviU
# MFe0JEOdXuE7Rn/OmWNSzQ+6kPNYDJQASQ974C3wUejJoMtGZEzoTbly/HufQTrd
# rhcL2aC69CxSN+idTXPLC9UT3xo4sFdOw+hXkmbXtoB1GDsd5p1TWFgRbnTXDkbM
# YMBWYVB6/Tk1bzwj4iTp4g0YrtB628FXPX/ko+JWZlv0Ea865S23w1uGlnDNVxIi
# +8oi74G5DM66Q6ENt6+3WoRGRrdoyE6uCh1haY+oPYSgumb0ozzrp8tw89TRKVrK
# KSXGBxlExEvjQ6dYwjGCGqowghqmAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNV
# BAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0
# IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQDDOrZz3oy4AP
# VlYV66EL0jANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgACh
# AoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAM
# BgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCACTH7yQWiDEIMzydsSmCSdozzf
# ew4KYc1aD0tuJynIHjANBgkqhkiG9w0BAQEFAASCAgAQ3SkgkTD/Vao5X3rJF4xg
# PhxDgiAVhEN/Tiy8JDiiI7DNCqDhvJ89fARPpf4OaZ924259S5wYSpENtEnRv+yp
# FG/G/GaYYgoYcmHLOfQdD+k+/Vd5SNscV1eQVeCgIc0rI5FbAA/3d1M5DQnRmWGA
# 1l4qIILg+My8bUcMjWogaTyWVYmKXg7LIzwA1SIwPW4LcnPFG01t10Qr/e3q1jmW
# MFoMcIO7aWyY4we7wG7CK7mSoO4H8KPMmFk4zJINIkWap+ocu6EU+VGkH7xLyzBJ
# udMd99yTcmsrxwjGbssLIbipndDq/ygawOgv9lARDyvn0hqm0tKNSH4V7o0ZUfPZ
# nfbUTkmLi0JSZlwpv5xvAgh/T5wbuGGW6ssgyjZHg8a5hQqupETagbw9+qMozz2N
# zGhSfO2oXCAXwUU08vSxN/TvPo/fiPB5kfd2l5SvFPcG/3LNpkPaxBTHrbLNarEE
# hdmQIkd++2yTK50aeCT/eHEOVK/fUg4jAkPpZuqmyLcmaYdBXrhjOSKFbcmJrV4U
# vrRk1a5kSDYU7NuD4mv5Rimsp3pt6niwh4cqoGYQNmF1O0V0B08/b2S4I6DOIgQJ
# 6eoRjP8kB6xT0C8wVM6SzYaZuhjhzZNZeKd2sOfshW1JAMEmGUf+eY4o+GBg56TK
# xJuJ8hXsG8oO4Jksl0VUKKGCF3cwghdzBgorBgEEAYI3AwMBMYIXYzCCF18GCSqG
# SIb3DQEHAqCCF1AwghdMAgEDMQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcNAQkQ
# AQSgaQRnMGUCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCCf3mNH7Opk
# s8w7LifpQtds0fYXQFzDWgLoO7heIjU2CQIRAOdmmsEqQOzbN9FOlkJDt3cYDzIw
# MjYxMDA4MTkxNDIxWqCCEzowggbtMIIE1aADAgECAhAIT9wzT35FTtvDD4/5khg1
# MA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2Vy
# dCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBp
# bmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwHhcNMjYwODA1MDAwMDAwWhcNMzcx
# MTA0MjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu
# Yy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFNIQTI1NiBSU0E0MDk2IFRpbWVzdGFtcCBS
# ZXNwb25kZXIgMjAyNiAxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# tnum8sn+zUr41JtMZbP9OMYw+HwJDpG5xkIu/lqcfNYmMX81YmsUiHLbh9ykpeWB
# GKTLhYBrAN9Tdg/QEzG32XcObmgIblnr0CoQ3WSAeDZ6nH6X6VkFyYkJw3QBJREw
# vm4UhLzSxmwPA7cFKRTEOMsmEEj6qJk/dqLEAL+oQYuOwE2UuiX1Vnul8YReIyWd
# 4kgLn9gq6LNXM0UplkR6jL/QHxmb6fMoGBJYbnaUI7XD6cKDpekK2SVMld4iDbze
# HDtOaaxldH5IxuNusQ69nd8/ZXEiB5Hbxj3RlK13cX1W4DlFXKdv/CEhM8Cj1vvl
# mvhNroyPdRGbbpBlgyf8Wdu5N6ByhFwURn0U6ozlPoxN22v+fviUhP+6DR547OZn
# pBMWDfei1f5sVGwiiW/KQTWOK97g+4RJpPzPNV4VYMAwO2jM2Aty2QYPVmOQTJm0
# msuXnJrSbl2gf9JylpkJlWXqk1Q4LJsxz+TELoQCZIljbgvTJgoPU2R12ydv8i1U
# qL/adelA0y7U9Pmmtbze9Xx3rtajC5SzQd1jgfwAwsa90v9YcSPdmeoyoBBA/27c
# CL237l5DTYYPDLQ4ON3OLTGWnvRb6jDrf/T75gMRfUzSLCBQfBusm9+mSWRlC/Df
# 6S/e9Q8i13CuhzOT2Jx+V/nlbXM4QoBwlUAhelwwJT0CAwEAAaOCAZUwggGRMAwG
# A1UdEwEB/wQCMAAwHQYDVR0OBBYEFBTJY4owLtRK+26U8+bjQH717M3iMB8GA1Ud
# IwQYMBaAFO9vU0rp5AZ8esrikFb2L9RJ7MtOMA4GA1UdDwEB/wQEAwIHgDAWBgNV
# HSUBAf8EDDAKBggrBgEFBQcDCDCBlQYIKwYBBQUHAQEEgYgwgYUwJAYIKwYBBQUH
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBdBggrBgEFBQcwAoZRaHR0cDov
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1w
# aW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3J0MF8GA1UdHwRYMFYwVKBSoFCGTmh0
# dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFt
# cGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNybDAgBgNVHSAEGTAXMAgGBmeBDAEE
# AjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAI3FOmEenVIK35msCYB+
# fShAsWvSYvLBItoNdAgQ2jIqrGsVsluXMJU/+mRebBc52s6lbKAvOVPXaizmKkML
# LflEEKDZQx4CkS2t8aHPjkXha3hYZ010htFa3dhNgmalH5vuWvh3tTCf4frTS7gP
# tGc4Z/xaPhQ2AB1mR8eEe/WbH0RWHvVIl6VwQ3+g5FKNfN2N/DWJkf13w2H+2Gfq
# Efbd35Ww8CvoYBjLNIDTadcPWdgsjsiOaK/7EsKJgLjUNIVgvcaFOLLQ/GlrA+0Z
# HJoFUbOr5SJN8zykPspXIXlpDJY/gqFUZRROeab9GVgmhbdOJcD/63RhxPahFUGb
# ckRONqMe6DYAv6/mOG0pWd3cPStsdcS7buj5DyniwRY8yooMH6ptx5vpP/pZzBPB
# eZD2U4IsthyxB5Jaa8qrOkB5z160TXiM5ADMspZ0TfD9MJoq0tFpFPssKRFhWeED
# YPvcUuN7U7lvcdHl4ezQ3NT/7Ffs1sR1yh/LRbdZ3B3Vc6q2WmD8mDC0p9kzl2o7
# 3iVtS946IkEj7FkRsZGww1teYxERROC745xrtjvcw9ZyyUjHZWGRIpJeMNsPquCD
# f0fkyHtB+J4AiNZqCQk23rxh+KbpyMTNVKItJ5l92Svl20U9NbqMBOVYl1h54NEY
# LJq1/xHWFKPNK903zJZA9P2DMIIGtDCCBJygAwIBAgIQDcesVwX/IZkuQEMiDDpJ
# hjANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjUwNTA3MDAwMDAwWhcNMzgwMTE0MjM1
# OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/
# BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYg
# U0hBMjU2IDIwMjUgQ0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# tHgx0wqYQXK+PEbAHKx126NGaHS0URedTa2NDZS1mZaDLFTtQ2oRjzUXMmxCqvkb
# sDpz4aH+qbxeLho8I6jY3xL1IusLopuW2qftJYJaDNs1+JH7Z+QdSKWM06qchUP+
# AbdJgMQB3h2DZ0Mal5kYp77jYMVQXSZH++0trj6Ao+xh/AS7sQRuQL37QXbDhAkt
# VJMQbzIBHYJBYgzWIjk8eDrYhXDEpKk7RdoX0M980EpLtlrNyHw0Xm+nt5pnYJU3
# Gmq6bNMI1I7Gb5IBZK4ivbVCiZv7PNBYqHEpNVWC2ZQ8BbfnFRQVESYOszFI2Wv8
# 2wnJRfN20VRS3hpLgIR4hjzL0hpoYGk81coWJ+KdPvMvaB0WkE/2qHxJ0ucS638Z
# xqU14lDnki7CcoKCz6eum5A19WZQHkqUJfdkDjHkccpL6uoG8pbF0LJAQQZxst7V
# vwDDjAmSFTUms+wV/FbWBqi7fTJnjq3hj0XbQcd8hjj/q8d6ylgxCZSKi17yVp2N
# L+cnT6Toy+rN+nM8M7LnLqCrO2JP3oW//1sfuZDKiDEb1AQ8es9Xr/u6bDTnYCTK
# IsDq1BtmXUqEG1NqzJKS4kOmxkYp2WyODi7vQTCBZtVFJfVZ3j7OgWmnhFr4yUoz
# ZtqgPrHRVHhGNKlYzyjlroPxul+bgIspzOwbtmsgY1MCAwEAAaOCAV0wggFZMBIG
# A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFO9vU0rp5AZ8esrikFb2L9RJ7MtO
# MB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIB
# hjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUH
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDov
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQw
# QwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZI
# AYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQAXzvsWgBz+Bz0RdnEwvb4LyLU0pn/N
# 0IfFiBowf0/Dm1wGc/Do7oVMY2mhXZXjDNJQa8j00DNqhCT3t+s8G0iP5kvN2n7J
# d2E4/iEIUBO41P5F448rSYJ59Ib61eoalhnd6ywFLerycvZTAz40y8S4F3/a+Z1j
# EMK/DMm/axFSgoR8n6c3nuZB9BfBwAQYK9FHaoq2e26MHvVY9gCDA/JYsq7pGdog
# P8HRtrYfctSLANEBfHU16r3J05qX3kId+ZOczgj5kjatVB+NdADVZKON/gnZruMv
# NYY2o1f4MXRJDMdTSlOLh0HCn2cQLwQCqjFbqrXuvTPSegOOzr4EWj7PtspIHBld
# NE2K9i697cvaiIo2p61Ed2p8xMJb82Yosn0z4y25xUbI7GIN/TpVfHIqQ6Ku/qjT
# Y6hc3hsXMrS+U0yy+GWqAXam4ToWd2UQ1KYT70kZjE4YtL8Pbzg0c1ugMZyZZd/B
# dHLiRu7hAWE6bTEm4XYRkA6Tl4KSFLFk43esaUeqGkH/wyW4N7OigizwJWeukcyI
# PbAvjSabnf7+Pu0VrFgoiovRDiyx3zEdmcif/sYQsfch28bZeUz2rtY/9TCA6TD8
# dC3JE3rYkrhLULy7Dc90G6e8BlqmyIjlgp2+VqsS9/wQD7yFylIz0scmbKvFoW2j
# NrbM1pD2T7m3XDCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZI
# hvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
# MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNz
# dXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVow
# YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290
# IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjww
# IjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J5
# 8soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMH
# hOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6
# Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQ
# ecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4b
# A3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9
# WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCU
# tNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvo
# ZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/J
# vNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCP
# orF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMB
# Af8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXr
# oq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRt
# MGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEF
# BQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgw
# BgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cH
# vZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8
# UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTn
# f+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxU
# jG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8j
# LfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3ww
# ggN4AgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0
# MDk2IFNIQTI1NiAyMDI1IENBMQIQCE/cM09+RU7bww+P+ZIYNTANBglghkgBZQME
# AgEFAKCB0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkF
# MQ8XDTI2MTAwODE5MTQyMVowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQUUdmr2gNJ
# c9hPQmaspIJI5rNpxDkwLwYJKoZIhvcNAQkEMSIEIJQosuxvm0PObBngFKDndUiV
# TqfBL48RtnF4ZyEj3ATuMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIC2gnaf0Ex+f
# 5y22xebpyWVnVa8EPx6nQswNISDhQev8MA0GCSqGSIb3DQEBAQUABIICAHjdCehf
# DJCHIFTpJ2pP0LeF/VHK0KzMq7NXuGizf3vEGvv5mLMyvTYfpa3SICOO/Nm8f/H5
# jHM5xRbSGBflTo2yJ9ym4Biv6I4n9vsS3Zj8vjYLXxgEnLrtCsVB/MSsni7QH0yM
# VjCasCG4m+qBlj9uxpCsGLQjbiWSZWWeChk0xmHeHD4uuo+29v7hGwEARdiJJjQY
# AMYIwEFs4qpMLviDy8gt5ACMDLjx1e++RrPVtLdVih2FTdaQ3SGzpvxhciLGZsw/
# wWqjk5u516YiEmjxtKzkqziqmq8hin9s+76K2cYuQF+pMqI1GLHX3ObC/YPXnJ9A
# G4XNH4ZfXHTZE5+vNAxpNzwjgoZpHwc4JwfO/jsmlNL5bJFgTLuh4Vz6viS1HvFV
# dZtUJSiV6NQbi/cYJGzgFpqxjkv+EN2kcPGrL0aQY/DGMlFm3Sf3jlWZqrlnmD7R
# lCd+kCcokst7WY7mN6qX4KZEwwaQWdH1Hu8RnOc6RJ+OHvEKluyC0qOtYgmCThxs
# EsWYzoXP/TE2Wa6OVszoo8EWj27L6ZXwXpeifXpW79c+MCvEXpY2UeZjHEq7D9RU
# CmCXCyzDIaVPUfMDJpA4vpBaLF7cJSoquLfg2ANX2ZZb46/nMjMDGv+TrVTSpl1z
# hpvR5DJNhYI0Ep4Ik6BNOYP/fI6WKrqzg3Tg
# SIG # End signature block