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
# MIInzQYJKoZIhvcNAQcCoIInvjCCJ7oCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCqVeKkA9mxosAS
# gw6sBZWxLeP+3Svl4Qo27dqq5dmZhqCCDYUwggYDMIID66ADAgECAhMzAAADTU6R
# phoosHiPAAAAAANNMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI4WhcNMjQwMzE0MTg0MzI4WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDUKPcKGVa6cboGQU03ONbUKyl4WpH6Q2Xo9cP3RhXTOa6C6THltd2RfnjlUQG+
# Mwoy93iGmGKEMF/jyO2XdiwMP427j90C/PMY/d5vY31sx+udtbif7GCJ7jJ1vLzd
# j28zV4r0FGG6yEv+tUNelTIsFmmSb0FUiJtU4r5sfCThvg8dI/F9Hh6xMZoVti+k
# bVla+hlG8bf4s00VTw4uAZhjGTFCYFRytKJ3/mteg2qnwvHDOgV7QSdV5dWdd0+x
# zcuG0qgd3oCCAjH8ZmjmowkHUe4dUmbcZfXsgWlOfc6DG7JS+DeJak1DvabamYqH
# g1AUeZ0+skpkwrKwXTFwBRltAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUId2Img2Sp05U6XI04jli2KohL+8w
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwMDUxNzAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# ACMET8WuzLrDwexuTUZe9v2xrW8WGUPRQVmyJ1b/BzKYBZ5aU4Qvh5LzZe9jOExD
# YUlKb/Y73lqIIfUcEO/6W3b+7t1P9m9M1xPrZv5cfnSCguooPDq4rQe/iCdNDwHT
# 6XYW6yetxTJMOo4tUDbSS0YiZr7Mab2wkjgNFa0jRFheS9daTS1oJ/z5bNlGinxq
# 2v8azSP/GcH/t8eTrHQfcax3WbPELoGHIbryrSUaOCphsnCNUqUN5FbEMlat5MuY
# 94rGMJnq1IEd6S8ngK6C8E9SWpGEO3NDa0NlAViorpGfI0NYIbdynyOB846aWAjN
# fgThIcdzdWFvAl/6ktWXLETn8u/lYQyWGmul3yz+w06puIPD9p4KPiWBkCesKDHv
# XLrT3BbLZ8dKqSOV8DtzLFAfc9qAsNiG8EoathluJBsbyFbpebadKlErFidAX8KE
# usk8htHqiSkNxydamL/tKfx3V/vDAoQE59ysv4r3pE+zdyfMairvkFNNw7cPn1kH
# Gcww9dFSY2QwAxhMzmoM0G+M+YvBnBu5wjfxNrMRilRbxM6Cj9hKFh0YTwba6M7z
# ntHHpX3d+nabjFm/TnMRROOgIXJzYbzKKaO2g1kWeyG2QtvIR147zlrbQD4X10Ab
# rRg9CpwW7xYxywezj+iNAc+QmFzR94dzJkEPUSCJPsTFMIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGZ4wghmaAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAANNTpGmGiiweI8AAAAA
# A00wDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIHlP
# x15KgcJa0sfeyq7rCA8Po1l9thledKNP58nfe0DnMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEAjT0Ue/mNfaqJ0otd4b3OrCVAR/sHDA6s3gHl
# 41/ENIyAiWNGxeGUeGB6oc/zPVCGkJ4CZnlRzLTmYi7PjwSFQ/rxWbQcVY5bCXd2
# n2lBFVj5kz30lxmJIIr2o4XKP/a11rQCDeWEHQggu0T0+zS7Yzuxu6j81cotNvV3
# Gj5qcxX6mnDokG40T4C6olRT/do8kSOtgsPTV84MNABCg/yBqkwqCgU2U+i+WBMh
# yfy49JliCS1PFtbg1YbGRLlNoUGl5mibylOHxqzUWKqvGdhvFpoFe+Fzqlg9nvaz
# eMN6xqmqOXrRu69vHqO3DtMBNJt/4X6tdEmXLJ7YRqnD8Jx7gqGCFygwghckBgor
# BgEEAYI3AwMBMYIXFDCCFxAGCSqGSIb3DQEHAqCCFwEwghb9AgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFYBgsqhkiG9w0BCRABBKCCAUcEggFDMIIBPwIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCBfUc+rK49MRloKOxCGizF1AedCFyhmduSs
# 4VMIAZSBWwIGZJMs6C2FGBIyMDIzMDcxODEyMDU0NS4xMlowBIACAfSggdikgdUw
# gdIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsT
# JE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMd
# VGhhbGVzIFRTUyBFU046ODZERi00QkJDLTkzMzUxJTAjBgNVBAMTHE1pY3Jvc29m
# dCBUaW1lLVN0YW1wIFNlcnZpY2WgghF4MIIHJzCCBQ+gAwIBAgITMwAAAbchJxoH
# oiqGRgABAAABtzANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ
# Q0EgMjAxMDAeFw0yMjA5MjAyMDIyMTRaFw0yMzEyMTQyMDIyMTRaMIHSMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3Nv
# ZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBU
# U1MgRVNOOjg2REYtNEJCQy05MzM1MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1T
# dGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx/3P
# V1A0E2RMGrcclyXKmNBwkiUMCCm+zDi8cl7TqfnfZEoaRyfRXunrk54DXA6g77w2
# yimeM+lVnz7iz9GF0wIw09AlehZ2RY6n/em4gSzV/M8GBMmt2bN3JRjOsCWggJjH
# vbaCZ2ls1/c/TLlnaBE7jyxYhjJh40lhyH5l8nNeqq+6+rsbw2bwgxCLxlrK/H7l
# 1IYEzlVgcHEISNcX0Q3pDDsDrRJpeMqOiNKlSalozgWZV5z3zk811KHyE54/a0vv
# zVIFqf3YNPX2180e/0fQUCYTYcZHCLMeQn/+YBl1LSzpuL9TA8ySwCJrlumcgME6
# hph1aRXSVUbms3T6W1HP3OOVof26F0ZMn5aI0o6vuZGeXNhADnd+1JIkGqwSlPMK
# /tzrvd92+W3HfLjw96vq9zpRHl7iRJmwfXjUiHGGZFHZKYRpu5go5N07tD++W8B9
# DG4Scn3c0vgthXUhbDw77E07qCWOsGkI5845NCBYkX4zr8g/K5ity/v0u7uVzrtL
# 5T0wS7Z2iDQy4kihgIyK5VpdLn4WY4mLJ+IJmbRUmwnnvTL2T+0RB7ppBH0zENbW
# nYXcdZ5uiVeP2y7vOusSU2UcAS+kWFyCgyeLKU1OJNnMvBSPtMfzeCe6DDUUIRVW
# HlT2XA0w8vUga7P/dT5FP3BkIElgisUk93qTxS0CAwEAAaOCAUkwggFFMB0GA1Ud
# DgQWBBSz8jk+DDQkBWEN1gHbI8gjyXi07DAfBgNVHSMEGDAWgBSfpxVdAF5iXYP0
# 5dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw
# MjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUt
# U3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB
# /wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOC
# AgEADZ1kzJuFXFJdNsnMWC82IwJXn8DKflS7MlTj3lgO1x5+iV3R6FH1AEYGEeYA
# y3XRZgIfPZZBTIzeZAPgiwd8QvUkTCAb20arvW5yf12ScsEcPfQm9tBGEgpy8VKe
# 9EIKlWmKUzwX9hpZcL1WJtJ5/89TYZYD7XOzrhUXEmod67LwqqtDJ8XbRIKd3zNF
# mXhhgFyOH0bQaOEkdQ8BgTHSvyNJaPKLajZYAcPoKmkG2TCdlJ/sDrwMvg7DAGmY
# UHf6+5uOA3BogJL5+QZW9Gc/ZmCaV+doBqzwzHuKAmpyKqrRDoCf7SsePWTGKw10
# iJPMWW7kbDZ6FekuuXn2S+yY41UAkeGd2nHef/SvvpMf7dY1L1RP2NvFCQT63a2G
# J/owrmSsUyeQEgIJH8NVxXWrD2yDqwwSjTBZJeVBhe9xBYDrl5XNKSizKWEiUhlk
# svcYdkPUtC0COnYDkXsLcjDg23sLtoMJ+kXGibdm6VGcUmiWU8dve6eP2ED3G9GI
# qdYwAylLgxkCiJXg7b3NYpl1TPKCnuAPhaMorXXkoInSi8Rx/3YCfAwBcyc2zWjQ
# sKzOD64OaJBmzl5HuPN0rNV8XXPtt8Ng08Am+bmlJB1vLfAg6w3+y1iMz4SRo+1T
# Ovw7IzCsb6OkUxuwHhx83q28h4a2u2SUwkW2cXrwJxlnkgAwggdxMIIFWaADAgEC
# AhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQg
# Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVa
# Fw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
# YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIC
# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7V
# gtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeF
# RiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3X
# D9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoP
# z130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+
# tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5Jas
# AUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/b
# fV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuv
# XsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg
# 8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzF
# a/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqP
# nhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEw
# IwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSf
# pxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBB
# MD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0Rv
# Y3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGC
# NxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w
# HwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmg
# R4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWlj
# Um9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEF
# BQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29D
# ZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEs
# H2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHk
# wo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinL
# btg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCg
# vxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsId
# w2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2
# zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23K
# jgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beu
# yOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/
# tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjm
# jJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBj
# U02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIC1DCCAj0CAQEwggEAoYHYpIHVMIHS
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRN
# aWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRo
# YWxlcyBUU1MgRVNOOjg2REYtNEJCQy05MzM1MSUwIwYDVQQDExxNaWNyb3NvZnQg
# VGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQDIZ0EYw5s4OWwYFmZJ
# RfahaB+pxqCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
# YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0G
# CSqGSIb3DQEBBQUAAgUA6GCYgjAiGA8yMDIzMDcxODEyNTAxMFoYDzIwMjMwNzE5
# MTI1MDEwWjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDoYJiCAgEAMAcCAQACAiB9
# MAcCAQACAhJIMAoCBQDoYeoCAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQB
# hFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEA
# CpoMrz5z6oGtBJ9M64Ftf82KGszsX44Ikz2+o0IEsqRLb/TttNPjkAJurh2SnwvB
# FqyPFiRCoq8HYevCQ668PfM0Ey3y+OcDXzEQQssZyTpx+O+18c8b7rFl9syyVeYc
# W71fPsxsdEIT03JUoi2zYxvNkol5sP58LCgdmOAxkoMxggQNMIIECQIBATCBkzB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAbchJxoHoiqGRgABAAAB
# tzANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
# MC8GCSqGSIb3DQEJBDEiBCCiS0KIa/+qiPYn8YFoDYEKb0g86Y8PvmSZRvmzMLVa
# jTCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIGwneNOyHtovyE16JgYvn/wg
# GfzNR+vv9DuuQzyJzXBBMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAG3IScaB6IqhkYAAQAAAbcwIgQghW0VzwY0C8ecEnidWcJR37ag
# qKv4z/ce8UV0JdNlcyQwDQYJKoZIhvcNAQELBQAEggIABCQqH6BVJDm+Zij7KIuX
# 8BhkxhJrMS+k+YXKdi8SlP/7GYKXNyYVEysBUc+hluYMvX9x9pytfeIY+uu3TX8h
# 8YkiGAG5q/AjHtkjSbD5PwygSgi6dOKVgUnJvcWkzRkvfFPGKeQutYU8LyZDjLhr
# v9XzKBI0WThlGrHJ4lLc9zJY669bB9ZOYO3tevM5+7eKHtzfhxR8wElFHCASTGa4
# vdessIybEPri5rv7ilIPDYJP5HMnEPkdCJkTiVdL+jq8/usKo6aDRCnrylryaFCe
# Ub4uAwiZ2fsHB3t5rRV5pfrPrTWtmiMkydGsyX17Gbb8IRAsogRw3+gAm6UKFBqA
# phL0ANPPYm8nV+qNRl3oMBH2TqUxwqSnxhz3CgbgZKtBYT19LdoxiHcy+nItcYTj
# /6mTRr6dB0YAR6zaso3jTI8O6FtqRYYjxWwIn1Okya5cy18EBzlD90gRWDZ9N0RM
# WAGl29qfzSCEhhPrOZQzVYrHwb+EuvSX9GA2a+yptzsQbx7QOrGRgZ1VOM3tJdHO
# eIwhjLkuggug6Cv2AqMDbXSId9Tl8mQABm6dvoRj+5e6cQb/yqW8b7vtd+AzIqmQ
# fSoz3D7eY1pE5m9Ct/uiHchnGaB07HsjoMZnxGsMdGhe2kgbaXIlKzU3IbjWx5hJ
# ejTxxTxEDq6c2gsgmuoeDvI=
# SIG # End signature block