Engines/Bicep/Invoke-AvmBicepCheckPolicy.ps1

function Invoke-AvmBicepCheckPolicy {
    <#
    .SYNOPSIS
        Evaluate the Bicep module's selected tests with PSRule for Azure.

    .DESCRIPTION
        Uses the package's PSRule options and rule/suppression directory
        to evaluate defaults and waf-aligned e2e tests with the required
        Reliability and AVM WAF Security baselines and the advisory Default
        and Security baselines. Files are tokenized in a temporary tree;
        original module files are never changed. Missing inputs, tools,
        baselines, or uninspectable results fail the check.

        The PSRule and PSRule.Rules.Azure versions pinned in avm.pins.jsonc are
        exact-version dependencies resolved through the shared tool cache.
        Missing modules are acquired before evaluation. Tokens are read from
        VALIDATE_SUBSCRIPTION_IDS or its TEST_SUBSCRIPTION_IDS alias (first
        entry matching the exact root reservation map), with
        VALIDATE_SUBSCRIPTION_ID as a General-only fallback, VALIDATE_TENANT_ID,
        VALIDATE_MANAGEMENT_GROUP_ID (or ARM_MGMTGROUP_ID), TOKEN_NAMEPREFIX,
        and localToken_* environment variables.

    .PARAMETER Context
        Bicep module context produced by Get-AvmModuleContext.

    .PARAMETER AllowPathFallback
        Permit a PATH-resolved Bicep CLI matching the pinned version.

    .PARAMETER Example
        Exact module-relative paths from the discovered policy tests.

    .PARAMETER Baseline
        Exact names from the four supported required and advisory baselines.

    .OUTPUTS
        pscustomobject with Engine, Tool, ToolPath, ToolSource, Status,
        RequiredBaselines, AdvisoryBaselines, TestsSelected,
        BaselinesExecuted, Evaluations, and Issues.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)]
        $Context,

        [switch] $AllowPathFallback,

        [ValidateNotNullOrEmpty()]
        [string[]] $Example,

        [ValidateNotNullOrEmpty()]
        [string[]] $Baseline
    )

    Set-StrictMode -Version 3.0
    $ErrorActionPreference = 'Stop'
    if ($Context.Ecosystem -ne 'bicep') {
        throw [System.ArgumentException]::new(
            "Invoke-AvmBicepCheckPolicy requires a bicep context (got Ecosystem='$($Context.Ecosystem)').")
    }

    $required = @('Azure.Pillar.Reliability', 'CB.AVM.WAF.Security')
    $advisory = @('Azure.Default', 'Azure.Pillar.Security')
    if ($PSBoundParameters.ContainsKey('Baseline')) {
        foreach ($name in $Baseline) {
            if (($required + $advisory) -cnotcontains $name) {
                throw [AvmConfigurationException]::new(
                    '-Baseline must select exact supported Bicep policy baseline names.')
            }
        }
        $required = @($required | Where-Object { $Baseline -ccontains $_ })
        $advisory = @($advisory | Where-Object { $Baseline -ccontains $_ })
    }
    $baselines = @(
        foreach ($name in $required) { [pscustomobject]@{ Name = $name; Required = $true } }
        foreach ($name in $advisory) { [pscustomobject]@{ Name = $name; Required = $false } }
    )
    $issues = [System.Collections.Generic.List[object]]::new()
    $evaluations = [System.Collections.Generic.List[object]]::new()
    $tests = [System.Collections.Generic.List[object]]::new()
    $testModulePaths = @{}
    $seen = [System.Collections.Generic.HashSet[string]]::new(
        $(if ($IsWindows) { [System.StringComparer]::OrdinalIgnoreCase }
            else { [System.StringComparer]::Ordinal }))
    # Only exceptions of the listed types carry author-facing messages; any
    # other failure is reported with the fixed fallback message.
    $addFailure = {
        param(
            [System.Management.Automation.ErrorRecord] $ErrorRecord,
            [string] $Path,
            [string] $Code,
            [type[]] $KnownType,
            [string] $Fallback,
            [string] $Baseline = ''
        )
        $exception = $ErrorRecord.Exception
        $message = $Fallback
        foreach ($type in $KnownType) {
            if ($exception -is $type) { $message = $exception.Message; break }
        }
        $issues.Add((New-AvmBicepPolicyIssue -Root $Context.Root -Path $Path `
                    -Code $Code -Message $message -Baseline $Baseline))
    }
    $repositoryRoot = $null
    $modules = @()
    try {
        $modules = @(Get-AvmMetadataScope -Context $Context -IncludeModuleDirectories)
    }
    catch {
        & $addFailure $_ $Context.Root 'avm.bicep.psrule-input' ([System.ArgumentException]) `
            "PSRule could not enumerate Bicep scopes ($($_.Exception.GetType().Name))."
    }
    foreach ($module in $modules) {
        $scope = Get-AvmBicepConventionScope -Path $module.Path
        if ($null -eq $scope) {
            $issues.Add((New-AvmBicepPolicyIssue -Root $Context.Root -Path $module.Path `
                        -Code 'avm.bicep.psrule-input' `
                        -Message 'PSRule requires modules under avm/res, avm/ptn, or avm/utl.'))
            continue
        }
        if ($null -ne $repositoryRoot -and
            $scope.RepositoryRoot -cne $repositoryRoot) {
            $issues.Add((New-AvmBicepPolicyIssue -Root $Context.Root -Path $module.Path `
                        -Code 'avm.bicep.psrule-input' `
                        -Message 'PSRule module scopes must share one repository root.'))
            continue
        }
        $repositoryRoot = $scope.RepositoryRoot
        try {
            foreach ($file in @(Get-AvmBicepPolicyTestFile -ModulePath $scope.Path `
                        -IncludeAllExamples:($PSBoundParameters.ContainsKey('Example')))) {
                if ($seen.Add($file.FullName)) {
                    $tests.Add($file)
                    $testModulePaths[$file.FullName] = $scope.ModuleRelativePath
                }
            }
        }
        catch {
            & $addFailure $_ $module.Path 'avm.bicep.psrule-input' ([AvmConfigurationException]) `
                "PSRule could not select test sources ($($_.Exception.GetType().Name))."
        }
    }
    if ($PSBoundParameters.ContainsKey('Example')) {
        $selected = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal)
        foreach ($path in $Example) {
            $relative = $path.Replace('\', '/')
            if ([string]::IsNullOrWhiteSpace($relative) -or
                [System.IO.Path]::IsPathRooted($relative) -or
                $relative -match '[:\x00-\x1f*?\[\]]' -or
                $relative.Split('/') -contains '..') {
                throw [AvmConfigurationException]::new(
                    '-Example must contain literal module-relative policy test paths without parent segments.')
            }
            $matched = @($tests | Where-Object {
                    [System.IO.Path]::GetRelativePath($Context.Root, $_.FullName).Replace('\', '/') -ceq $relative
                })
            if ($matched.Count -ne 1) {
                throw [AvmConfigurationException]::new(
                    "Policy example '$relative' did not match exactly one discovered e2e main.test.bicep file.")
            }
            $null = $selected.Add($matched[0].FullName)
        }
        $tests = [System.Collections.Generic.List[object]]@($tests | Where-Object { $selected.Contains($_.FullName) })
    }
    if ($tests.Count -eq 0) {
        $issues.Add((New-AvmBicepPolicyIssue -Root $Context.Root -Path $Context.Root `
                    -Code 'avm.bicep.psrule-input-missing' `
                    -Message 'No defaults or waf-aligned main.test.bicep sources were selected for PSRule.'))
    }

    $tool = $null
    $compiler = $null
    $configuration = $null
    $verified = @{}
    if ($issues.Count -eq 0) {
        try {
            $tool = Import-AvmBicepPolicyModule -ModuleRoot $Context.Root
        }
        catch {
            & $addFailure $_ $Context.Root 'avm.bicep.psrule-module' ([AvmConfigurationException]) `
                'Required PSRule module versions could not be loaded.'
        }
    }
    if ($issues.Count -eq 0) {
        try {
            $configuration = Get-AvmBicepPolicyConfiguration
        }
        catch {
            & $addFailure $_ $Context.Root 'avm.bicep.psrule-config' ([AvmConfigurationException]) `
                'PSRule configuration could not be loaded; inspect ps-rule.yaml and the installed modules.'
        }
    }
    if ($issues.Count -eq 0) {
        foreach ($baselineDefinition in $baselines) {
            try {
                $verified[$baselineDefinition.Name] = Get-AvmBicepPolicyBaseline `
                    -Configuration $configuration -Name $baselineDefinition.Name
            }
            catch {
                & $addFailure $_ $Context.Root 'avm.bicep.psrule-baseline' ([AvmConfigurationException]) `
                    "PSRule baseline '$($baselineDefinition.Name)' could not be inspected." $baselineDefinition.Name
            }
        }
    }
    if ($issues.Count -eq 0 -and
        -not $PSCmdlet.ShouldProcess($Context.Root, 'Stage Bicep tests for PSRule evaluation')) {
        $issues.Add((New-AvmBicepPolicyIssue -Root $Context.Root -Path $Context.Root `
                    -Code 'avm.bicep.psrule-not-run' `
                    -Message 'PSRule sources were not staged; no baseline ran.'))
    }
    if ($issues.Count -eq 0) {
        try {
            $compiler = Resolve-AvmTool -Name 'bicep' -ModuleRoot $Context.Root -AllowPathFallback:$AllowPathFallback
        }
        catch {
            $knownCompilerFailure = [type[]]@([AvmToolException], [AvmConfigurationException])
            & $addFailure $_ $Context.Root 'avm.bicep.psrule-compiler' $knownCompilerFailure `
                'The pinned Bicep compiler could not be resolved.'
        }
    }

    if ($issues.Count -eq 0) {
        $previousBicep = [Environment]::GetEnvironmentVariable('PSRULE_AZURE_BICEP_PATH')
        try {
            $env:PSRULE_AZURE_BICEP_PATH = $compiler.Path
            foreach ($test in @($tests | Sort-Object -Culture 'en-US' -Property FullName)) {
                try {
                    $tokens = Get-AvmBicepPolicyToken -RepositoryRoot $repositoryRoot `
                        -ModulePath $testModulePaths[$test.FullName]
                    $files = @(Get-AvmBicepPolicySource -Path $test.FullName `
                            -RepositoryRoot $repositoryRoot -Tokens $tokens)
                }
                catch {
                    & $addFailure $_ $test.FullName 'avm.bicep.psrule-source' ([AvmConfigurationException]) `
                        "PSRule could not read a selected test or local reference ($($_.Exception.GetType().Name))."
                    continue
                }

                $stage = $null
                try {
                    $stage = [System.IO.Directory]::CreateTempSubdirectory('avm-psrule-')
                    Copy-AvmBicepPolicySource -Files $files -Destination $stage.FullName
                    $relative = [System.IO.Path]::GetRelativePath(
                        $repositoryRoot, $test.FullName)
                    $stagePath = Join-Path $stage.FullName $relative
                    if (-not [System.IO.File]::Exists($stagePath)) {
                        throw [AvmConfigurationException]::new(
                            'The selected PSRule input was not staged.')
                    }
                    foreach ($baselineDefinition in $baselines) {
                        try {
                            $records = @(Invoke-AvmBicepPolicyBaseline `
                                    -StageRoot $stage.FullName -InputPath $relative `
                                    -OptionPath $configuration.OptionPath `
                                    -RulePath $configuration.RulePath `
                                    -Baseline $baselineDefinition.Name)
                            $result = ConvertFrom-AvmBicepPolicyResult -Records $records `
                                -Baseline $verified[$baselineDefinition.Name] `
                                -StagePath $stagePath `
                                -SourcePath $test.FullName -ModuleRoot $Context.Root
                            foreach ($issue in $result.Issues) { $issues.Add($issue) }
                            if (-not $result.Valid) { continue }
                            $evaluations.Add([pscustomobject]@{
                                    File            = [System.IO.Path]::GetRelativePath(
                                        $Context.Root, $test.FullName).Replace('\', '/')
                                    Baseline        = $baselineDefinition.Name
                                    Required        = $baselineDefinition.Required
                                    Records         = $records.Count
                                    ExpandedRecords = $result.ExpandedRecords
                                    ProcessedRules  = $result.ProcessedRules
                                    FailedRules     = $result.Failures.Count
                                })
                            foreach ($failure in $result.Failures) {
                                $severity = if ($baselineDefinition.Required) { 'error' } else { 'warning' }
                                $message = "PSRule baseline '$($baselineDefinition.Name)' failed rule '$($failure.RuleName)' for target type '$($failure.TargetType)'."
                                $issues.Add((New-AvmBicepPolicyIssue -Root $Context.Root `
                                            -Path $test.FullName -Baseline $baselineDefinition.Name `
                                            -RuleName $failure.RuleName -TargetType $failure.TargetType `
                                            -Severity $severity -Code 'avm.bicep.psrule-rule' `
                                            -Message $message))
                            }
                        }
                        catch {
                            $message = "PSRule baseline '$($baselineDefinition.Name)' could not evaluate this tokenized Bicep test ($($_.Exception.GetType().Name)); verify the source and configuration."
                            $issues.Add((New-AvmBicepPolicyIssue -Root $Context.Root `
                                        -Path $test.FullName -Baseline $baselineDefinition.Name `
                                        -Code 'avm.bicep.psrule-evaluation' `
                                        -Message $message))
                        }
                    }
                }
                catch {
                    $issues.Add((New-AvmBicepPolicyIssue -Root $Context.Root -Path $test.FullName `
                                -Code 'avm.bicep.psrule-staging' `
                                -Message 'PSRule could not stage the tokenized Bicep test and its local references.'))
                }
                finally {
                    if ($null -ne $stage) {
                        try {
                            [System.IO.Directory]::Delete($stage.FullName, $true)
                        }
                        catch {
                            $issues.Add((New-AvmBicepPolicyIssue -Root $Context.Root `
                                        -Path $test.FullName -Code 'avm.bicep.psrule-cleanup' `
                                        -Message ("PSRule temporary files could not be removed from '{0}'; remove them before retrying." -f $stage.FullName)))
                        }
                    }
                }
            }
        }
        finally {
            if ($null -eq $previousBicep) {
                [Environment]::SetEnvironmentVariable('PSRULE_AZURE_BICEP_PATH', $null)
            }
            else {
                $env:PSRULE_AZURE_BICEP_PATH = $previousBicep
            }
        }
    }

    return [pscustomobject][ordered]@{
        Engine            = 'bicep'
        Tool              = if ($null -ne $tool) { $tool.Name } else { 'PSRule.Rules.Azure (not run)' }
        ToolPath          = if ($null -ne $tool) { $tool.Path } else { $null }
        ToolSource        = if ($evaluations.Count -gt 0) { 'powershell-module' } else { 'not-run' }
        Status            = if (@($issues | Where-Object { $_.Severity -eq 'error' }).Count -gt 0) { 'fail' } else { 'pass' }
        RequiredBaselines = $required
        AdvisoryBaselines = $advisory
        TestsSelected     = $tests.Count
        BaselinesExecuted = $evaluations.Count
        Evaluations       = $evaluations.ToArray()
        Issues            = $issues.ToArray()
    }
}

# SIG # Begin signature block
# MIInRAYJKoZIhvcNAQcCoIInNTCCJzECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBcCqKegAugZ630
# zfqRvS/aRBYQI5pZwW+ERKUxykqhhKCCDLowggX1MIID3aADAgECAhMzAAACHU0Z
# yE7XD1dIAAAAAAIdMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD
# b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQzWhcNMjcwNDE1MTg1
# OTQzWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD
# VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB
# DwAwggEKAoIBAQDQvewXxx9gZZFC6Ys1WBay8BJ8kGA4JQnH5CMafqOASlTpK9H8
# o5ZXTXt0caVQTNMUPt445wXYD+dFtaKWTwDn1I52oUSrC9vJin1Gsqt+zyKJL5Dg
# 3eQXbQNR61DmMy20GLTIO3SFed9Rfi/ophgCLGFLDR3r0KvHjwMb/jYWS0celV/4
# Lz27LfAekm8v9E5IXaeiXbAUYZKK090n4CVl3JBtbN+9DtI9SNu/yjvozW52/u7R
# X/Ttpa/KDlpuokZ+Zcbvmtd9ur9gFLvZzh41o9MsE/clQtdaFWGvuo6Jua/ntpgk
# ey3E5/vBFe+MJPG6phdnuo6r57ZudCudiI1bAgMBAAGjggGbMIIBlzAOBgNVHQ8B
# Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O
# BBYEFH6QuMwqcPG0hQlQ6c5jCtTTLrVeMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQL
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAxMis1MDc1NTkw
# HwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEwYAYDVR0fBFkwVzBVoFOg
# UYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0
# JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNybDBtBggrBgEFBQcBAQRh
# MF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv
# Y2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy
# dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBKTbYOjzwTG/DXGaz9
# s6+fQeaTtDcFmMY+5UyVFCyj7Pv+5i37qfX8lSL/tBIfYQfWsMuBQlfZurJD6r4H
# VJ2CeH+1fgiq8dcHdVKoZ3Sa2qXoX3cq9iS8cVb06B7+5/XJ7I0OxHH9fDsvJ3T3
# w5V/ZtAIFmLrl+P0CtG+92uzRsn0nTbdFjOkLMLWPLAU3THohKRlSEMgFJpPkm5n
# 5UAZ35xX6FWCrDLsSKb555bTifwa8mJBwdlof0bmfYidH+dxZ1FdDxvLnNl9zeKs
# A4kejaaIqqIPguhwAti5Ql7BlTNoJNwxCvBmqW2MQLnCkYN/VVUsR3V2x/rcTNzo
# Bf/Z/SpROvdaA2ZOOd1uioXJt3tdLQ7vHpqpib0KfWr/FWXW10q38VxfCnRQBqzb
# SuztR7nEMuzX7Ck+B/XaPDXd1qh72+QYyB0Z2VzWmO9zsnb9Uq/dwu8LGeQqnyu6
# 7SDGACvnXii2fb9+US492VTnXSnFKyqwgzUyFMtZK1/sHYTv6bG4TtQUygQxTN+Z
# V+aJIlKO2MqZ7bKrAnOzS9m6NgoTdWOq11bTOZwKlIEV/EhV9SWkDmdpR/hPPT2v
# 6TEj4F8PT/zHjRezIU5c/DGlt/VhY/pK0XkJtEyMmmS1BMtjU/rqBZVMIm3dnxQs
# /TBByr+Cf8Z1r7aifQVQ+WSqzjCCBr0wggSloAMCAQICEzMAAAA5O7Y3Gb8GHWcA
# AAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX
# YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg
# Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl
# IEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoXDTM2MDMyMjIyMTMwNFow
# VzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo
# MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAyNDCCAiIwDQYJ
# KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeqlRYHNa265v4IY9fH8TKh
# emHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo0dtS/EW6I/yEL/bLSY8h
# KpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATvQVL4tcf03aTycsz8QeCd
# M0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a1uv1zerOYMnsneRRwCbp
# yW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1FyQfK0fVkaya8SmVHQ/t
# Of23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfOGSWHIIV4YrTJTT6PNty5
# REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7ttOu1bVnXfHaqPYl2rPs
# 20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJuz2MXMCt7iw7lFPG9LXK
# Gjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxSCwyoGIq0PhaA7Y+VPct5
# pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOmVQop36wUVUYklUy++vDW
# eEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3SkE/xIkgpfl22MM1itkZ
# 35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGC
# NxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPXLQaUEggxMBkGCSsGAQQB
# gjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU
# ci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2Ny
# bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAx
# MV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0
# dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx
# MV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAFJQfOChP7onn6fLI
# MKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D5W4wMwYeLystcEqfkjz4
# NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBYnbu0+THSuVHTe0VTTPVh
# ily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSIvgn0JksVBVMYVI5QFu/q
# hnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6aR9y34aiM1qmxaxBi6OU
# nyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4wPKC5OmHm1DQIt/MNokbb
# H3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7RTX8AdBPo0I6OEojf39z
# uFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK/fg8B2qjW88MT/WF5V5u
# vZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSKYBv0VisCzfxgeU+dquXW
# 9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkwYTu/9dLeH2pDqeJZAABV
# DWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVTQl0v4q8J/AUmQN5W4n10
# 1cY2L4A7GTQG1h32HHAvfQESWP0xghngMIIZ3AIBATBuMFcxCzAJBgNVBAYTAlVT
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv
# c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w
# DQYJYIZIAWUDBAIBBQCggZAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwLwYJ
# KoZIhvcNAQkEMSIEIGQPP4UtbXuMm/3vUEvyWut/iWsRjzN78YU1iqG8/kQTMEIG
# CisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAuuL60r52mhmz36+T
# /Gpo5uhpEh+hiqyNvBOSVVQrzop34ugbAVxT/g7xT0IHdVGMWshme09fZo+DuBaD
# 5yTT25PYpNe5r5x0DQP7a7+aSvFI6NAIkBKpKpLyIGhdRW1HY7C2aappBNuBi+ta
# DplLmLrJ+NyclNghNh12X4FF9y0Ev1/wr8twM5OAgNJfLNox3aPBNm5zw+XoKs/k
# Vps8mbhmIH2JPzJJvEUaYNgFw1GivdKaCtJTQZHkIxOcRWJzxwLQ6t4lOgKlNS6Q
# wFuu/aQFGBESF4xqsBjEaLpBJKcU94rr+enc7HUnCdACSZz8rHnOMDCQyw5xh0Ts
# SRWu6qGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCCF5gGCSqGSIb3DQEHAqCCF4kw
# gheFAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFF
# MIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCDJiWlPua/yfYX9
# aW4k0OKaLLa+w4pfOJE5A3y/vmr+sQIGaq0ro1WIGBMyMDI2MTAyMTA4NTExOC4w
# MjJaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExp
# bWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo2QjA1LTA1RTAtRDk0NzEl
# MCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEf4wggcoMIIF
# EKADAgECAhMzAAACEUUYOZtDz/xsAAEAAAIRMA0GCSqGSIb3DQEBCwUAMHwxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv
# c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDgxNDE4NDgxM1oXDTI2MTEx
# MzE4NDgxM1owgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# LTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEn
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjZCMDUtMDVFMC1EOTQ3MSUwIwYDVQQD
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEAz7m7MxAdL5Vayrk7jsMo3GnhN85ktHCZEvEcj4BIccHK
# d/NKC7uPvpX5dhO63W6VM5iCxklG8qQeVVrPaKvj8dYYJC7DNt4NN3XlVdC/vove
# JuPPhTJ/u7X+pYmV2qehTVPOOB1/hpmt51SzgxZczMdnFl+X2e1PgutSA5CAh9/X
# z5NW0CxnYVz8g0Vpxg+Bq32amktRXr8m3BSEgUs8jgWRPVzPHEczpbhloGGEfHaR
# OmHhVKIqN+JhMweEjU2NXM2W6hm32j/QH/I/KWqNNfYchHaG0xJljVTYoUKPpcQD
# uhH9dQKEgvGxj2U5/3Fq1em4dO6Ih04m6R+ttxr6Y8oRJH9ZhZ3sciFBIvZh7E2Y
# FXOjP4MGybSylQTPDEFAtHHgpkskeEUhsPDR9VvWWhekhQx3qXaAKh+AkLmz/hpE
# 3e0y+RIKO2AREjULJAKgf+R9QnNvqMeMkz9PGrjsijqWGzB2k2JNyaUYKlbmQweO
# absCioiY2fJbimjVyFAGk5AeYddUFxvJGgRVCH7BeBPKAq7MMOmSCTOMZ0Sw6zyN
# x4Uhh5Y0uJ0ZOoTKnB3KfdN/ba/eKHFeEhi3WqAfzTxiy0rMvhsfsXZK7zoclqaR
# vVl8Q48J174+eyriypY9HhU+ohgiYi4uQGDDVdTDeKDtoC/hD2Cn+ARzwE1rFfEC
# AwEAAaOCAUkwggFFMB0GA1UdDgQWBBRifUUDwOnqIcvfb53+yV0EZn7OcDAfBgNV
# HSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5o
# dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBU
# aW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwG
# CCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRz
# L01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNV
# HRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIH
# gDANBgkqhkiG9w0BAQsFAAOCAgEApEKdnMeIIUiU6PatZ/qbrwiDzYUMKRczC4Bp
# /XY1S9NmHI+2c3dcpwH2SOmDfdvIIqt7mRrgvBPYOvJ9CtZS5eeIrsObC0b0ggKT
# v2wrTgWG+qktqNFEhQeipdURNLN68uHAm5edwBytd1kwy5r6B93klxDsldOmVWtw
# /ngj7knN09muCmwr17JnsMFcoIN/H59s+1RYN7Vid4+7nj8FcvYy9rbZOMndBzsT
# iosF1M+aMIJX2k3EVFVsuDL7/R5ppI9Tg7eWQOWKMZHPdsA3ZqWzDuhJqTzoFSQS
# hnZenC+xq/z9BhHPFFbUtfjAoG6EDPjSQJYXmogja8OEa19xwnh3wVufeP+ck+/0
# gxNi7g+kO6WaOm052F4siD8xi6Uv75L7798lHvPThcxHHsgXqMY592d1wUof3tL/
# eDaQ0UhnYCU8yGkU2XJnctONnBKAvURAvf2qiIWDj4Lpcm0zA7VuofuJR1Tpuyc5
# p1ja52bNZBBVqAOwyDhAmqWsJXAjYXnssC/fJkee314Fh+GIyMgvAPRScgqRZqV1
# 6dTBYvoe+w1n/wWs/ySTUsxDw4T/AITcu5PAsLnCVpArDrFLRTFyut+eHUoG6UYZ
# fj8/RsuQ42INse1pb/cPm7G2lcLJtkIKT80xvB1LiaNvPTBVEcmNSvFUM0xrXZXc
# YcxVXiYwggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3
# DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIw
# MAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAx
# MDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l
# LVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# 5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/
# XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1
# hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7
# M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3K
# Ni1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy
# 1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF80
# 3RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQc
# NIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahha
# YQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkL
# iWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV
# 2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIG
# CSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUp
# zxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBT
# MFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jv
# c29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYI
# KwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGG
# MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186a
# GMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3Br
# aS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsG
# AQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcN
# AQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1
# OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYA
# A7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbz
# aN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6L
# GYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3m
# Sj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0
# SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxko
# JLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFm
# PWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC482
# 2rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7
# vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDWTCC
# AkECAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExp
# bWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo2QjA1LTA1RTAtRDk0NzEl
# MCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsO
# AwIaAxUAKyp8q2VdgAq1VGkzd7PZwV6zNc2ggYMwgYCkfjB8MQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt
# ZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsFAAIFAO6Cfx0wIhgPMjAyNjEw
# MjEwMDAwMjlaGA8yMDI2MTAyMjAwMDAyOVowdzA9BgorBgEEAYRZCgQBMS8wLTAK
# AgUA7oJ/HQIBADAKAgEAAgIHbgIB/zAHAgEAAgITIjAKAgUA7oPQnQIBADA2Bgor
# BgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAID
# AYagMA0GCSqGSIb3DQEBCwUAA4IBAQBLyZ7f2qMdvPDEkz7WqusUrWJ8a4Q/g07j
# pktWJ52RT2qpLqR2L+gTlhTp22oNGWZ5xMVpnzCRKFCs2v2ZysM9N0sK0Dk+Mcvp
# MQtcIOpFMzoqjGAiwIMM7mm7GgbRYvQugBXh2VYvfOwipldVPwXrSRq1gJRE/M8g
# NiH8ldPyphtv12PyVkgsMAytMLlN8BfJQ1F7V2XG5ZxruN0EVSgMm9DFBA3D0PYJ
# 5LLOFyR34zKs4BGAcelRaT9tD/3/0FEZBkd3iabvBUKpbtRx09IsYGFuaFK9N/EE
# WwKZZwcDqkqBo4ph28Kqt8KngF0S6bPZBrl18O6WbgaMWYfIsaeAMYIEDTCCBAkC
# AQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG
# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAIRRRg5m0PP
# /GwAAQAAAhEwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG
# 9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQg3QwHAFOOgrnkbsNrISguumdxnHeYYfoo
# CAFg8ScCAvIwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCAsrTOpmu+HTq1a
# XFwvlhjF8p2nUCNNCEX/OWLHNDMmtzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0
# YW1wIFBDQSAyMDEwAhMzAAACEUUYOZtDz/xsAAEAAAIRMCIEILn5PGJimpfsiLD9
# GWrfswn2JQvRMRZEyAPrCXK2tH3pMA0GCSqGSIb3DQEBCwUABIICAFxls9O4WO/g
# iWL6KU9mwkoEvi1+ZNvqBBG0vwwQuSG4bw6lNj70oZm0KknkITEItD9CW/KNcU47
# Rohxg4bGvLfo7CcBirhiV3uZ99EO9A5CEJqFJm6RRCffCKYC1gJGNx24TEfi6KLT
# 2xfFN0gRs/LYdf3GieFdm3Vfqj+WaIhS89UwBgLZWy0X9J6TUz5WtHQ+sjwUNUD0
# amhoY0CHoiXgkCQQHtxa5XWjzwz/iZEdNW2laNfWiaP1LoNXwzASsXvtK1/ku6ek
# HmgMtQx8tyb5HMfqMUD0A7MOFkuo7swD6LHRceYGE7EHnw69tUJbxLqoHFgKRoAw
# RDk+m3kPdMtL3ygbhTl4izUthyzz7PvYQ9MtKJgHH3j9+kUlsb9Lb883COGDYhnv
# BI9cTOdshAzPrO1DeHKL+QLwGxgspmIly333AwDecvStdGdWY8Ya5ZXPJlRJsj8f
# ZrwVda0krKQxUW94CTRb03XAr+Lq7Uzqr3xLnbaStC9ZJ4utu5yDRhTMGy6zB1YE
# qmf1WMH4A4HmFEloPhW26YnvjxR54z7A4cTao+oS6ZUzJkQttq5w3uAh6K8hGTGu
# 1SMSKa+7J2+r6VStDJ1u6bXFgkTwh+svnBSXSL05gOlQlc4P9B/+ZUHdRTUKuyAU
# aS3go0BZJXKFyxJVGcaZc9ylN3VlUc4O
# SIG # End signature block