Framework/Helpers/RemoteReportHelper.ps1

Set-StrictMode -Version Latest

class RemoteReportHelper
{
    hidden static [string[]] $IgnoreScanParamList = "DoNotOpenOutputFolder";
    hidden static [string[]] $AllowedServiceScanParamList = "organizationName", "ResourceGroupNames";
    hidden static [string[]] $AllowedOrganizationScanParamList = "organizationName";
    hidden static [int] $MaxServiceParamCount = [RemoteReportHelper]::IgnoreScanParamList.Count + [RemoteReportHelper]::AllowedServiceScanParamList.Count;
    hidden static [int] $MaxOrganizationParamCount = [RemoteReportHelper]::IgnoreScanParamList.Count + [RemoteReportHelper]::AllowedOrganizationScanParamList.Count;
    hidden static [System.Security.Cryptography.SHA256Managed] $sha256AlgForMasking = [System.Security.Cryptography.SHA256Managed]::new();
    hidden static [AIOrgTelemetryStatus] $AIOrgTelemetryState = [AIOrgTelemetryStatus]::Undefined;
    hidden static [string] $TelemetryKey = "";

    static [FeatureGroup] GetFeatureGroup([SVTEventContext[]] $SVTEventContexts)
    {
        if(($SVTEventContexts | Measure-Object).Count -eq 0 -or $null -eq $SVTEventContexts[0].FeatureName) {
            return [FeatureGroup]::Unknown
        }
        $feature = $SVTEventContexts[0].FeatureName.ToLower()
        if($feature.Contains("organization")){
            return [FeatureGroup]::Organization
        } else{
            return [FeatureGroup]::Service
        }
    }

    static [ServiceScanKind] GetServiceScanKind([string] $command, [hashtable] $parameters)
    {
        $parameterNames = [array] $parameters.Keys
        if($parameterNames.Count -gt [RemoteReportHelper]::MaxServiceParamCount)
        {
            return [ServiceScanKind]::Partial;
        }
        $validParamCounter = 0;
        foreach($parameterName in $parameterNames)
        {
            if ([RemoteReportHelper]::AllowedServiceScanParamList.Contains($parameterName))
            {
                $validParamCounter += 1
            }
            elseif ([RemoteReportHelper]::IgnoreScanParamList.Contains($parameterName))
            {
                # Ignoring
            }
            else
            {
                return [ServiceScanKind]::Partial;
            }
        }

        if ($validParamCounter -eq 1)
        {
            return [ServiceScanKind]::Organization;
        }
        elseif ($validParamCounter -eq 2)
        {
            return [ServiceScanKind]::ResourceGroup;
        }
        else
        {
            return [ServiceScanKind]::Partial;
        }
    }

    static [OrganizationScanKind] GetOrganizationScanKind([string] $command, [hashtable] $parameters)
    {
        $parameterNames = [array] $parameters.Keys
        if($parameterNames.Count -gt [RemoteReportHelper]::MaxOrganizationParamCount)
        {
            return [OrganizationScanKind]::Partial;
        }
        $validParamCounter = 0;
        foreach($parameterName in $parameterNames)
        {
            if ([RemoteReportHelper]::AllowedOrganizationScanParamList.Contains($parameterName))
            {
                $validParamCounter += 1
            }
            elseif ([RemoteReportHelper]::IgnoreScanParamList.Contains($parameterName))
            {
                # Ignoring
            }
            else
            {
                return [OrganizationScanKind]::Partial;
            }
        }

        if ($validParamCounter -eq 1)
        {
            return [OrganizationScanKind]::Complete;
        }
        else
        {
            return [OrganizationScanKind]::Partial;
        }
    }

    static [OrganizationControlResult] BuildOrganizationControlResult([ControlResult] $controlResult, [ControlItem] $control)
    {
        $result = [OrganizationControlResult]::new()
        $result.ControlId = $control.ControlId
        $result.ControlIntId = $control.Id
        $result.ControlSeverity = $control.ControlSeverity
        $result.ActualVerificationResult = $controlResult.ActualVerificationResult
        $result.AttestationStatus = $controlResult.AttestationStatus
        $result.VerificationResult = $controlResult.VerificationResult
        $result.HasRequiredAccess = $controlResult.CurrentSessionContext.Permissions.HasRequiredAccess
        $result.IsBaselineControl = $control.IsBaselineControl
        #add PreviewBaselineFlag
        $result.IsPreviewBaselineControl = $control.IsPreviewBaselineControl
        $result.MaximumAllowedGraceDays = $controlResult.MaximumAllowedGraceDays
        if($control.Tags.Contains("OwnerAccess")  -or $control.Tags.Contains("GraphRead"))
        {
            $result.HasOwnerAccessTag = $true
        }

        $result.UserComments = $controlResult.UserComments

        if($null -ne $controlResult.StateManagement -and $null -ne $controlResult.StateManagement.AttestedStateData) {
            $result.AttestedBy = $controlResult.StateManagement.AttestedStateData.AttestedBy
            $result.Justification = $controlResult.StateManagement.AttestedStateData.Justification
            $result.AttestedState = [JsonHelper]::ConvertToJsonCustomCompressed($controlResult.StateManagement.AttestedStateData.DataObject)
            $result.AttestedDate = $controlResult.StateManagement.AttestedStateData.AttestedDate
            $result.AttestationExpiryDate = $controlResult.StateManagement.AttestedStateData.ExpiryDate

        }
        if($null -ne $controlResult.StateManagement -and $null -ne $controlResult.StateManagement.CurrentStateData) {
            $result.CurrentState = [JsonHelper]::ConvertToJsonCustomCompressed($controlResult.StateManagement.CurrentStateData.DataObject)
        }
        return $result;
    }

    static [ServiceControlResult] BuildServiceControlResult([ControlResult] $controlResult, [bool] $isNestedResource, [ControlItem] $control)
    {
        $result = [ServiceControlResult]::new()
        $result.IsNestedResource = $isNestedResource
        if ($isNestedResource)
        {
            $result.NestedResourceName = $controlResult.ChildResourceName
        }
        else
        {
            $result.NestedResourceName = $null
        }
        $result.ControlId = $control.ControlID
        $result.ControlIntId = $control.Id
        $result.ControlSeverity = $control.ControlSeverity
        $result.ActualVerificationResult = $controlResult.ActualVerificationResult
        $result.AttestationStatus = $controlResult.AttestationStatus
        $result.VerificationResult = $controlResult.VerificationResult
        $result.HasRequiredAccess = $controlResult.CurrentSessionContext.Permissions.HasRequiredAccess
        $result.IsBaselineControl = $control.IsBaselineControl
        #add PreviewBaselineFlag
        $result.IsPreviewBaselineControl = $control.IsPreviewBaselineControl
        $result.UserComments = $controlResult.UserComments
        $result.MaximumAllowedGraceDays = $controlResult.MaximumAllowedGraceDays
        if($control.Tags.Contains("OwnerAccess")  -or $control.Tags.Contains("GraphRead"))
        {
            $result.HasOwnerAccessTag = $true
        }

        if($null -ne $controlResult.StateManagement -and $null -ne $controlResult.StateManagement.AttestedStateData) {
            $result.AttestedBy = $controlResult.StateManagement.AttestedStateData.AttestedBy
            $result.Justification = $controlResult.StateManagement.AttestedStateData.Justification
            $result.AttestedState = [JsonHelper]::ConvertToJsonCustomCompressed($controlResult.StateManagement.AttestedStateData.DataObject)
            $result.AttestedDate = $controlResult.StateManagement.AttestedStateData.AttestedDate
            $result.AttestationExpiryDate = $controlResult.StateManagement.AttestedStateData.ExpiryDate
        }
        if($null -ne $controlResult.StateManagement -and $null -ne $controlResult.StateManagement.CurrentStateData) {
            $result.CurrentState = [JsonHelper]::ConvertToJsonCustomCompressed($controlResult.StateManagement.CurrentStateData.DataObject)
        }
        return $result;
    }

    static [ScanSource] GetScanSource()
    {        
        $settings = [ConfigurationManager]::GetAzSKSettings();
        [string] $laSource = $settings.LASource;
        if([string]::IsNullOrWhiteSpace($laSource)){
            return [ScanSource]::SpotCheck
        }
        if($laSource.Equals("CICD", [System.StringComparison]::OrdinalIgnoreCase)){
            return [ScanSource]::VSO
        }
        if($laSource.Equals("CA", [System.StringComparison]::OrdinalIgnoreCase)){
            return [ScanSource]::Runbook
        }
        return [ScanSource]::SpotCheck
    }

    static [string] GetAIOrgTelemetryKey()
    {
        if(-not [string]::IsNullOrEmpty([RemoteReportHelper]::TelemetryKey))
        {
            return [RemoteReportHelper]::TelemetryKey
        }
        $settings = [ConfigurationManager]::GetAzSKConfigData();
        [RemoteReportHelper]::TelemetryKey = $settings.ControlTelemetryKey
        [guid]$key = [guid]::Empty
        # Trying to parse [RemoteReportHelper]::TelemetryKey into $key and then checking that it is not empty
        if([guid]::TryParse([RemoteReportHelper]::TelemetryKey, [ref] $key) -and ![guid]::Empty.Equals($key))
        {
            return [RemoteReportHelper]::TelemetryKey;
        }
        [RemoteReportHelper]::TelemetryKey = [ConfigurationManager]::GetAzSKSettings().LocalControlTelemetryKey
        return [RemoteReportHelper]::TelemetryKey;
    }

    static [bool] IsAIOrgTelemetryEnabled()
    {
        if([RemoteReportHelper]::AIOrgTelemetryState -eq [AIOrgTelemetryStatus]::Enabled)
        {
            return $true
        }
        elseif([RemoteReportHelper]::AIOrgTelemetryState -eq [AIOrgTelemetryStatus]::Disabled)
        {
            return $false
        }
        #If AIOrgTelemetryState is Undefined then evaluate
        $settings = [ConfigurationManager]::GetAzSKConfigData();
        $orgTelemetryKey = $settings.ControlTelemetryKey
        [guid]$key = [guid]::Empty
        # Trying to parse [RemoteReportHelper]::TelemetryKey into $key and then checking that it is not empty
        if([guid]::TryParse($orgTelemetryKey, [ref] $key) -and ![guid]::Empty.Equals($key))
        {
            if($settings.EnableControlTelemetry)
            {
                [RemoteReportHelper]::AIOrgTelemetryState = [AIOrgTelemetryStatus]::Enabled
                return $true
            }
            else 
            {
                [RemoteReportHelper]::AIOrgTelemetryState = [AIOrgTelemetryStatus]::Disabled
                return $false
            }
        }
        if([ConfigurationManager]::GetAzSKSettings().LocalEnableControlTelemetry)
        {
            [RemoteReportHelper]::AIOrgTelemetryState = [AIOrgTelemetryStatus]::Enabled
            return $true
        }
        else 
        {
            [RemoteReportHelper]::AIOrgTelemetryState = [AIOrgTelemetryStatus]::Disabled
            return $false
        }
    }
    
    static [string] Mask([psobject] $toMask)
    {
        $maskBytes = [System.Text.Encoding]::UTF8.GetBytes($toMask.ToString().ToLower())
        $maskBytes = ([RemoteReportHelper]::sha256AlgForMasking).ComputeHash($maskBytes)
        $take = 16
        $sb = [System.Text.StringBuilder]::new($take)
        for($i = 0; $i -lt ($take/2); $i++){
            $x = $sb.Append($maskBytes[$i].ToString("x2"))
        }
        return $sb.ToString();
    }
}

# SIG # Begin signature block
# MIIjoQYJKoZIhvcNAQcCoIIjkjCCI44CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCqVeKkA9mxosAS
# gw6sBZWxLeP+3Svl4Qo27dqq5dmZhqCCDYEwggX/MIID56ADAgECAhMzAAAB32vw
# LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn
# s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw
# PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS
# yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG
# 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh
# EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH
# tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS
# 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp
# TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok
# t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4
# b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao
# mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD
# Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt
# VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G
# CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+
# Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82
# oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVdjCCFXICAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN
# BglghkgBZQMEAgEFAKCBsDAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgeU/HXkqB
# wlrSx97KrusIDw+jWX22GV50o0/nyd97QOcwRAYKKwYBBAGCNwIBDDE2MDSgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRyAGmh0dHBzOi8vd3d3Lm1pY3Jvc29mdC5jb20g
# MA0GCSqGSIb3DQEBAQUABIIBAD54rlw4zFUifEiOeWQ/tnmuX7awcEmzgiysJrDO
# 4FVeprMmOTFmbZJc8kO71DlNh/BvItQF1vVwhf8di1W62taCuIe9drz6uQUPKDNO
# GK6CslReD/hf34vy8WjqTvppMLjRR8DApVvmdh97mJ9+EznELr38NsWTEbfASPiX
# AO3kkn+BLXn43OT5wiH53NHgeCt9GGd7gNPsbyKiHMrhSky3o3b0ajOKYwwJc0ll
# bGy2B2nCPtK+BYiwDpWfR1gnvFF7PpemP68KZfR3tB19MiK93mrcPHotRWeMv1Cs
# XRbNzJ3DG6lRPMUcQJIIXxj2sRTe0tSyh7efkEzfPyaTiPChghL+MIIS+gYKKwYB
# BAGCNwMDATGCEuowghLmBgkqhkiG9w0BBwKgghLXMIIS0wIBAzEPMA0GCWCGSAFl
# AwQCAQUAMIIBWQYLKoZIhvcNAQkQAQSgggFIBIIBRDCCAUACAQEGCisGAQQBhFkK
# AwEwMTANBglghkgBZQMEAgEFAAQgo3WyyOUydlRkDMG2DOAzN/ANRNknXezq3N9B
# xm/zD2cCBmExH3P4txgTMjAyMTA5MTUxMTA5NDEuOTUyWjAEgAIB9KCB2KSB1TCB
# 0jELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMk
# TWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjpGQzQxLTRCRDQtRDIyMDElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaCCDk0wggT5MIID4aADAgECAhMzAAABQCMZ1l7e
# lSQxAAAAAAFAMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
# QSAyMDEwMB4XDTIwMTAxNTE3MjgyNloXDTIyMDExMjE3MjgyNlowgdIxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29m
# dCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRT
# UyBFU046RkM0MS00QkQ0LUQyMjAxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0
# YW1wIFNlcnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCufWsz
# cerVL03TPxH5gqpm7bnKSTk6VPxOy7C10FbIMJEWgBKT18HqyIKiUWFcGHJ6Phzf
# IjA3RTIlYE5MCMe144hiN8KnHnf2tuAEjn8FMe0L6pwFPt+0+SdO1Cfz2U05yk/v
# R+5hVkuhCwOcuMbHG1b95V7BHlDQjWZZB8nLnE596WTk5aPPdhXgcq2rIhHMll39
# HNxjzDqqbOhI2xgh2+WJPZ55BlvJhN0lCxGjMgpMwsIlQF9WOjDZ8kwO3MMH1cQ5
# 1+E9bO9Q5p1iCqqHSWyUBHs1X3QUWZmBlYBGsbyPtmdWcLkw5c5L80jnxLjzJyy6
# DSk3Y0YsuTZhaPELAgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQUNUMcLiZ3RiCOjNKq
# dWz454QtDmcwHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0f
# BE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJv
# ZHVjdHMvTWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4w
# TDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0
# cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNV
# HSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEAYwxSraBC4IL3Cvhi
# EhJ8/Khto1hXc6/hjBaxJ8jP+PXFo31O8sAHYHE+LYK1FuBsFR/jyfTvJF5kifC7
# avy/Aug0bZO1jN7LTUNHKOOw2iIcX1S5EsXIpkKGQoLej2vQ7LbHRhiNSkPFUKFn
# mrlwB/DzzjA/SJRxicooafx4nSfCmvvOv9OW74c6NcNP0LvnhpLgpQU2bwPuLC69
# ZbNI5WXtcxZ27zYGedOYHuzY5x/cjhp0bN2LFDlnHFrfM4C8rOtX7QdxVAhjdJAn
# 0/OMNGXMK+IxOHEDwVQhEvcWdiq9yFaQShnjDxLsWwZY2VctZDt8cxveXiCO54fI
# 7inq1TCCBnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgx
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1p
# Y3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcw
# MTIxMzY1NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs
# /BOX9fp/aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUd
# zgkTjnxhMFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAy
# WGBG8lhHhjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJy
# GiGKr0tkiVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqx
# qPJ6Kgox8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4W
# nAEFTyJNAgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU
# 1WM6XIoxkPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEw
# CwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/o
# olxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNy
# b3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt
# MjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5j
# cnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIB
# FjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQu
# aHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8A
# UwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG
# 4Jg/gXEDPZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m8
# 7WtUVwgrUYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/
# 8jd9Wj8c8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kp
# vLb9BOFwnzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlK
# cWOdeyFtw5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsi
# OCC1JeVk7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw
# 4TtxCd9ddJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcun
# Caw5u+zGy9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1
# wC9UJyH3yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvH
# Ia9Zta7cRDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2g
# UDXa7wknHNWzfjUeCLraNtvTX4/edIhJEqGCAtcwggJAAgEBMIIBAKGB2KSB1TCB
# 0jELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMk
# TWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjpGQzQxLTRCRDQtRDIyMDElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAQqXmHvITpjsyl+Yy
# kRtDOQlyUVOggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN
# BgkqhkiG9w0BAQUFAAIFAOTsGEgwIhgPMjAyMTA5MTUxNDU5MjBaGA8yMDIxMDkx
# NjE0NTkyMFowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA5OwYSAIBADAKAgEAAgIi
# eAIB/zAHAgEAAgIRGDAKAgUA5O1pyAIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgor
# BgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUA
# A4GBAEstGHivWbTrkY0ivVvW78hPJqgTcnCLFDHjjMNBlbAogr4nHfws395dta4Y
# mkr5fSvAMsGy4DTsUCjbzPqaLF3liYTDc2xhaktFMl9guRupl56YRDVOI3Xou4Y9
# 7DVLMuHAg3hOVW1lBLIUh/AS9Y13z/iLpb7+3hhg4RgnimloMYIDDTCCAwkCAQEw
# gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAFAIxnWXt6VJDEA
# AAAAAUAwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B
# CRABBDAvBgkqhkiG9w0BCQQxIgQgSWD2TdYVSflK13WPyWyilnRWCLYkkf9dzHy0
# RYwsV3gwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCAvNrC16szSpFwk7/Ny
# 8lPt2j/JynxFmxFJOqq2AgiXgzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwAhMzAAABQCMZ1l7elSQxAAAAAAFAMCIEIMG1HGF9B2LLg65kPQJg
# z82R4/dSEbyXmZYj3/Zs4pQCMA0GCSqGSIb3DQEBCwUABIIBAHQLtSnAiqSROyVy
# HoEKcjrm3r4cNIKhjfFwIsV94Au1jpBk9oQ5cxP1MTqxBKwB6S9BuHB8upo0IZF+
# /pHYMW4Ckppl7qSP7AAwwFSslZTNpLI2Ptgl1TWqWFow5d1A0isd0YMy+S+jDEKs
# wpmxjE+jOjuE0gAYK/PAtH4yZEAVjgK3qnvCXSBLbJJyOgMHANhwK+6iJYJmPuMA
# rdYIwH9QTqJxIsM77kILbCKKGj46o3lIH1fDnYQ0+eTGQ1fXpSrA8oOSEQzoCD8R
# 9ak4rW1h+Sf/ubFyzNNzV6piIgQ/QG08B50MsRSM67OIbwdeZXuzpKVa+BSshqXy
# 39YswcI=
# SIG # End signature block