Framework/Core/SVT/CommonSVTResourceResolver.ps1
Set-StrictMode -Version Latest class CommonSVTResourceResolver { [string] $ResourceType = ""; [ResourceTypeName] $ResourceTypeName = [ResourceTypeName]::All; [string] $organizationName [string] $organizationId [string] $projectId [psobject] $feedDefnsObj = $null [bool] $UseIncrementalScan = $false [bool] $IsAutomatedFixUndoCmd = $false; [DateTime] $IncrementalDate = 0 [PSObject] $organizationContext CommonSVTResourceResolver($organizationName, $organizationId, $projectId, $organizationContext, $IsAutomatedFixUndoCmd) { $this.organizationName = $organizationName; $this.organizationId = $organizationId; $this.projectId = $projectId; $this.organizationContext = $organizationContext $this.IsAutomatedFixUndoCmd = $IsAutomatedFixUndoCmd if($PSCmdlet.MyInvocation.BoundParameters["IncrementalScan"]){ $this.UseIncrementalScan = $true if (-not [string]::IsNullOrWhiteSpace($PSCmdlet.MyInvocation.BoundParameters["IncrementalDate"])) { $this.IncrementalDate = $PSCmdlet.MyInvocation.BoundParameters["IncrementalDate"] } else { $this.IncrementalDate = [datetime] 0 } } } [SVTResource[]] LoadResourcesForScan($projectName, $repoNames, $secureFileNames, $feedNames, $environmentNames, $ResourceTypeName, $MaxObjectsToScan, $isServiceIdBasedScan) { #Get resources [System.Collections.Generic.List[SVTResource]] $SVTResources = @(); if ($repoNames.Count -gt 0 -or ($ResourceTypeName -in ([ResourceTypeName]::Repository, [ResourceTypeName]::All,[ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User_CommonSVTResources, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources) -and !$isServiceIdBasedScan) ) { #Write-Host "Getting repository configurations..." -ForegroundColor cyan if ($ResourceTypeName -in([ResourceTypeName]::Repository, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources) -and $repoNames.Count -eq 0) { $repoNames += "*"; } $repoObjList = @(); #if rtn Build_Release_SvcConn_AgentPool_VarGroup_User_CommonSVTResources and resource name not provided (neither * nor any name) no need to fetch this resource if($repoNames.Count -ne 0){ $repoObjList += $this.FetchRepositories($projectName, $repoNames); } if ($repoObjList.count -gt 0 -and [Helpers]::CheckMember($repoObjList[0], "Id")) { $maxObjScan = $MaxObjectsToScan foreach ($repo in $repoObjList) { $resourceId = "organization/{0}/project/{1}/repository/{2}" -f $this.organizationId, $this.projectId, $repo.id; $SVTResources.Add($this.AddSVTResource($repo.name, $projectName, "ADO.Repository", $resourceId, $repo, $repo.webUrl)); if (--$maxObjScan -eq 0) { break; } } $repoObjList = $null; } } ##Get SecureFiles if ($secureFileNames.Count -gt 0 -or ($ResourceTypeName -in ([ResourceTypeName]::SecureFile, [ResourceTypeName]::All,[ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User_CommonSVTResources, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources) -and !$isServiceIdBasedScan) ) { if ($ResourceTypeName -in([ResourceTypeName]::SecureFile, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources) -and $secureFileNames.Count -eq 0) { $secureFileNames += "*" } # Here we are fetching all the secure files in the project. $secureFileObjList = @(); #if rtn Build_Release_SvcConn_AgentPool_VarGroup_User_CommonSVTResources and resource name not provided (neither * nor any name) no need to fetch this resource if($secureFileNames.Count -ne 0){ $secureFileObjList += $this.FetchSecureFiles($projectName, $secureFileNames); } if ($secureFileObjList.count -gt 0 -and [Helpers]::CheckMember($secureFileObjList[0], "Id")) { $maxObjScan = $MaxObjectsToScan foreach ($securefile in $secureFileObjList) { $resourceId = "organization/{0}/project/{1}/securefile/{2}" -f $this.organizationId, $this.projectId, $securefile.Id; $secureFileLink = "https://dev.azure.com/{0}/{1}/_library?itemType=SecureFiles&view=SecureFileView&secureFileId={2}&path={3}" -f $this.organizationName, $projectName, $securefile.Id, $securefile.Name; $SVTResources.Add($this.AddSVTResource($securefile.Name, $projectName, "ADO.SecureFile", $resourceId, $securefile, $secureFileLink)); if (--$maxObjScan -eq 0) { break; } } $secureFileObjList = $null; } } #Get feeds if ($feedNames.Count -gt 0 -or ($ResourceTypeName -in ([ResourceTypeName]::Feed, [ResourceTypeName]::All,[ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User_CommonSVTResources, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources) -and !$isServiceIdBasedScan) ) { #Write-Host "Getting feed configurations..." -ForegroundColor cyan if ($ResourceTypeName -in([ResourceTypeName]::Feed, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources) -and $feedNames.Count -eq 0) { $feedNames += "*" } $feedObjList = @(); #if rtn Build_Release_SvcConn_AgentPool_VarGroup_User_CommonSVTResources and resource name not provided (neither * nor any name) no need to fetch this resource if($feedNames.Count -ne 0){ $feedObjList += $this.FetchFeeds($projectName, $feedNames); } if ($feedObjList.count -gt 0 -and [Helpers]::CheckMember($feedObjList[0], "Id")) { $maxObjScan = $MaxObjectsToScan foreach ($feed in $feedObjList) { $resourceId = "organization/{0}/project/{1}/feed/{2}" -f $this.organizationId, $this.projectId, $feed.id; $resourceLink = "https://dev.azure.com/{0}/{1}/_packaging?_a=feed&feed={2}" -f $this.organizationName, $projectName, $feed.name; $SVTResources.Add($this.AddSVTResource($feed.name, $projectName, "ADO.Feed", $resourceId, $feed, $resourceLink)); if (--$maxObjScan -eq 0) { break; } } $feedObjList = $null; } } #Get $EnvironmentNames if ($environmentNames.Count -gt 0 -or ($ResourceTypeName -in ([ResourceTypeName]::Environment, [ResourceTypeName]::All, [ResourceTypeName]::Build_Release_SvcConn_AgentPool_VarGroup_User_CommonSVTResources, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources) -and !$isServiceIdBasedScan)) { #Write-Host "Getting feed configurations..." -ForegroundColor cyan if ($ResourceTypeName -in([ResourceTypeName]::Environment, [ResourceTypeName]::SvcConn_AgentPool_VarGroup_CommonSVTResources) -and $environmentNames.Count -eq 0) { $environmentNames += "*" } $environmentObjList = @(); #if rtn Build_Release_SvcConn_AgentPool_VarGroup_User_CommonSVTResources and resource name not provided (neither * nor any name) no need to fetch this resource if($environmentNames.Count -ne 0){ $environmentObjList += $this.FetchEnvironments($projectName, $environmentNames, $MaxObjectsToScan); } if ($environmentObjList.count -gt 0 -and [Helpers]::CheckMember($environmentObjList[0], "Id")) { $maxObjScan = $MaxObjectsToScan foreach ($environment in $environmentObjList) { $resourceId = "organization/{0}/project/{1}/environment/{2}" -f $this.organizationId, $this.projectId, $environment.id; $resourceLink = "https://dev.azure.com/{0}/{1}/_environments/{2}?view=resources" -f $this.organizationName, $environment.project.id, $environment.id; $SVTResources.Add($this.AddSVTResource($environment.name, $projectName, "ADO.Environment", $resourceId, $environment, $resourceLink)); if (--$maxObjScan -eq 0) { break; } } $environmentObjList = $null; } } return $SVTResources; } hidden [PSObject] FetchRepositories($projectName, $repoNames) { try { # Here we are fetching all the repositories in the project and then filtering out. $repoDefnURL = ""; $repoDefnURL = "https://dev.azure.com/$($this.organizationName)/$projectName/_apis/git/repositories?api-version=6.1-preview.1" $repoDefnsObj = [WebRequestHelper]::InvokeGetWebRequest($repoDefnURL); if ($repoNames -ne "*") { $repoDefnsObj = $repoDefnsObj | Where-Object { $repoNames -contains $_.name } } else{ if($this.UseIncrementalScan){ $timestamp = (Get-Date) $incrementalScanHelperObj = [IncrementalScanHelper]::new($this.organizationName, $projectName, $this.IncrementalDate, $true, $timestamp) $incrementalScanHelperObj.SetContext($this.projectId, $this.organizationContext) $repoDefnsObj = $incrementalScanHelperObj.GetModifiedCommonSvtFromAudit("GitRepositories",$repoDefnsObj) } } return $repoDefnsObj; } catch { return $null; } } hidden [PSObject] FetchFeeds($projectName, $feedNames) { try { #Fetching project and org scoped feeds if($null -eq $this.feedDefnsObj) { #When controls undo fix is called, resources need to be fetched from deleted list (only for controls ids in RevertDeletedResourcesControlList) if($this.IsAutomatedFixUndoCmd){ $feedDefnURL = 'https://feeds.dev.azure.com/{0}/_apis/Packaging/FeedRecycleBin?api-version=6.0-preview.1&includeUrls=false' -f $this.organizationName } elseif($PSCmdlet.MyInvocation.BoundParameters["CheckOwnerAccess"]){ $feedDefnURL = 'https://feeds.dev.azure.com/{0}/_apis/packaging/feeds?feedRole=administrator&api-version=6.0-preview.1&includeUrls=false' -f $this.organizationName } else{ $feedDefnURL = 'https://feeds.dev.azure.com/{0}/_apis/packaging/feeds?api-version=6.0-preview.1&includeUrls=false' -f $this.organizationName } $this.feedDefnsObj = [WebRequestHelper]::InvokeGetWebRequest($feedDefnURL); } $feedsList = @() #current project scoped feeds $projectScopedFeeds = $this.feedDefnsObj | where-object {"Project" -in $_.PSobject.Properties.name} $feedsList += $projectScopedFeeds | where-object {$_.Project.id -eq $this.projectId} #org scoped feeds - Project property does not exist of org scoped feeds $feedsList += $this.feedDefnsObj | where-object {"Project" -notin $_.PSobject.Properties.name} if ($feedNames -ne "*") { $feedsList = $feedsList | Where-Object { $feedNames -contains $_.name } } else{ if($this.UseIncrementalScan){ $timestamp = (Get-Date) $incrementalScanHelperObj = [IncrementalScanHelper]::new($this.organizationName, $projectName, $this.IncrementalDate, $true, $timestamp) $incrementalScanHelperObj.SetContext($this.projectId, $this.organizationContext) $feedsList = $incrementalScanHelperObj.GetModifiedCommonSvtFromAudit("Feed",$feedsList) } } #Following piece of code is to get a list of all feeds that wont be scanned due to insufficient privileges, will be used only for control fix if($PSCmdlet.MyInvocation.BoundParameters["CheckOwnerAccess"]){ $totalFeedsURL = 'https://feeds.dev.azure.com/{0}/_apis/packaging/feeds?api-version=6.0-preview.1&includeUrls=false' -f $this.organizationName $totalFeedsObj = [WebRequestHelper]::InvokeGetWebRequest($totalFeedsURL); $totalFeeds=@(); $totalFeeds += $totalFeedsObj | where-object {"Project" -in $_.PSobject.Properties.name -and $_.Project.id -eq $this.projectId} $totalFeeds += $totalFeedsObj | where-object {"Project" -notin $_.PSobject.Properties.name} $nonScannedResources = @(); #get all feeds not being scanned $nonScannedResources += ((Compare-Object $totalFeeds $feedsList -Property name,id) | select -ExpandProperty name) #update the list with the corresponding resource links $nonScannedResources = $nonScannedResources | foreach{ $_ = "https://dev.azure.com/{0}/{1}/_packaging?_a=feed&feed={2}" -f $this.organizationName, $projectName, $_; $_; } try{ #saving this in an env variable as we have to access it while saving a list of these resources in logs. $env:nonScannedResources +=$nonScannedResources } catch{ #TODO: in case of higher number of feeds, this env variable may not be stored #in such cases the scan should work properly with owner access feeds even if nonscannedresources.json cannot be formed if($_ -like "Environment variable name or value is too long"){ $env:nonScannedResources = $null; } } if([Helpers]::CheckMember($feedsList[0],"id")){ $feedCntWithOwnerAccess = $feedsList.Count } else{ $feedCntWithOwnerAccess=0 } Write-Host "Found $($totalFeeds.Count) feeds. Current user has owner access on $($feedCntWithOwnerAccess) feeds. $($totalFeeds.Count - $feedCntWithOwnerAccess) feeds will not be scanned due to insufficient permissions." -ForegroundColor Yellow } return $feedsList } catch { return $null; } } hidden [PSObject] FetchSecureFiles($projectName, $secureFileNames) { $secureFileDefnURL = "https://dev.azure.com/$($this.organizationName)/$projectName/_apis/distributedtask/securefiles?api-version=6.1-preview.1" try { $secureFileDefnObj = [WebRequestHelper]::InvokeGetWebRequest($secureFileDefnURL); if ($secureFileNames -ne "*") { $secureFileDefnObj = $secureFileDefnObj | Where-Object { $secureFileNames -contains $_.name } } else{ if($this.UseIncrementalScan){ $timestamp = (Get-Date) $incrementalScanHelperObj = [IncrementalScanHelper]::new($this.organizationName, $projectName, $this.IncrementalDate, $true, $timestamp) $incrementalScanHelperObj.SetContext($this.projectId, $this.organizationContext) $secureFileDefnObj = $incrementalScanHelperObj.GetModifiedCommonSvtFromAudit("SecureFile",$secureFileDefnObj) } } return $secureFileDefnObj; } catch { return $null; } } hidden [PSObject] FetchEnvironments($projectName, $environmentNames, $MaxObjectsToScan) { try { if ($MaxObjectsToScan -eq 0) { $topNQueryString = '&$top=10000' } else { $topNQueryString = '&$top={0}' -f $MaxObjectsToScan } # Here we are fetching all the environments in the project. $environmentDefnURL = ("https://dev.azure.com/{0}/{1}/_apis/distributedtask/environments?api-version=6.0-preview.1" + $topNQueryString) -f $this.organizationName, $projectName; $environmentDefnsObj = [WebRequestHelper]::InvokeGetWebRequest($environmentDefnURL); if ($environmentNames -ne "*") { $environmentDefnsObj = $environmentDefnsObj | Where-Object { $environmentNames -contains $_.name } } else{ if($this.UseIncrementalScan){ $timestamp = (Get-Date) $incrementalScanHelperObj = [IncrementalScanHelper]::new($this.organizationName, $projectName, $this.IncrementalDate, $true, $timestamp) $incrementalScanHelperObj.SetContext($this.projectId, $this.organizationContext) $environmentDefnsObj = $incrementalScanHelperObj.GetModifiedCommonSvtFromAudit("Environment",$environmentDefnsObj) } } return $environmentDefnsObj; } catch { return $null; } } [SVTResource] AddSVTResource([string] $name, [string] $resourceGroupName, [string] $resourceType, [string] $resourceId, [PSObject] $resourceDetailsObj, $resourceLink) { $svtResource = [SVTResource]::new(); $svtResource.ResourceName = $name; if ($resourceGroupName) { $svtResource.ResourceGroupName = $resourceGroupName; } $svtResource.ResourceType = $resourceType; $svtResource.ResourceId = $resourceId; $svtResource.ResourceTypeMapping = ([SVTMapping]::AzSKADOResourceMapping | Where-Object { $_.ResourceType -eq $resourceType } | Select-Object -First 1) if ($resourceDetailsObj) { $svtResource.ResourceDetails = $resourceDetailsObj; $svtResource.ResourceDetails | Add-Member -Name 'ResourceLink' -Type NoteProperty -Value $resourceLink; } else { $svtResource.ResourceDetails = New-Object -TypeName psobject -Property @{ ResourceLink = $resourceLink } } return $svtResource; } } # SIG # Begin signature block # MIInoQYJKoZIhvcNAQcCoIInkjCCJ44CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDUWUBdiXuXolQH # 8o1znBzF1ckIxcWoxRebnWNgdPsDRaCCDYEwggX/MIID56ADAgECAhMzAAACUosz # qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I # sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O # L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA # v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o # RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8 # q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3 # uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp # kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7 # l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u # TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1 # o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti # yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z # 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf # 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK # WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW # esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F # 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS # 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/BvW1taslScxMNelDNMYIZdjCCGXICAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgKSYpb1RF # N2wURkGRq7zh5Rvi4YIczh/AQKTuGK6926swQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQARQXDcdoZ8oYcMoBE7MTNWlUJRoOYHl5fpGiWE8+tZ # el50qE/4BZKm/xxntc7v+wYuz2Z3tu93f0HDkksfwbInfU5CO+6OsFRqdGWuAzb9 # 9G/iLQMuWBWor5XaARIoIpJYKgyDOXRgGE83ox7O9xNNh6q9PhLQgni4CtjjpL4K # i/XrxtFYUuAken1voKzXEKxdSeb1S14SzPWfdh2htZZZ1BDImLSiiyAGcoNUkr7h # cSN4CxhgDjVUOoNZi3tdUtqLtvJ6oihTl6MjiSkoYdL40VrIKZdcG4REO2zDKKa/ # w7Hr+3S1wH31JQQ0qvPz4IaPyxOVkaTX8Br9nmF0td7NoYIXADCCFvwGCisGAQQB # gjcDAwExghbsMIIW6AYJKoZIhvcNAQcCoIIW2TCCFtUCAQMxDzANBglghkgBZQME # AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIPfzL410oN+ouPzSh9MtcXEt/DG7bPIZC+2+eW3S # x/iCAgZiFl+rnXwYEzIwMjIwMzE1MDgzNTE1LjEzM1owBIACAfSggdCkgc0wgcox # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p # Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg # RVNOOkFFMkMtRTMyQi0xQUZDMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNloIIRVzCCBwwwggT0oAMCAQICEzMAAAGWSVti4S/d908AAQAAAZYw # DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN # MjExMjAyMTkwNTEzWhcNMjMwMjI4MTkwNTEzWjCByjELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046QUUyQy1FMzJCLTFB # RkMxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0G # CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDSH2wQC2+t/jzA6jL6LZMhDNJG0nv1 # cUqe+H4MGKyEgRZUwp1YsHl1ITGyi8K9rkPRKKKAi0lT8g0b1GIipkWc8qCtE3wi # bxoNR4mCyrvgEsXutnbxI1obx8cMfa2XgchG/XBGZcFtGd0UQvXkxUYvokfG1TyB # MqnIZvQ2LtcmGj86laPRNuRodkEM7VVUO2oMSHJbaTNj1b2kAC8sqlytH1zmfrQp # TA3rZOyEmywT43DRfsNlXmkNKMiW7BafNnHZLGHGacpimE4doDMur3yiH/qCCx2P # O4pIqkA6WLGSN8yhYavcQZRFVtsl/x/IiuL0fxPGpQmRc84m41yauncveNh/5/14 # MqsZ7ugY1ix8fkOYgJBlLss8myPhaMA6qcEB/RWWqcCfhyARNjCcmBNGNXeMgKyZ # /+e3bCOlXmWeDtVJDLmOtzEDBLmkg2/etp3T9hOX+LodYwdBkY2noCDEzPWVa834 # AmkJvR6ynEeBGj6ouWifpXxaobBdasb0+r/9eYr+T00yrLFn16rrTULnVzkW7lLy # XWEousvzYnul3HPCQooQS4LY1HBKTyTSftGX56ZgOz7Rk+esvbcr+NjLvBBy7Xeo # mgkuw1F/Uru7lZ9AR+EQbpg2pvCHSarMQQHbf1GXPhlDTHwkeskRiz5jPjTr1Wz/ # f+9CZx5ovtTF0QIDAQABo4IBNjCCATIwHQYDVR0OBBYEFNLfCNksLmWtIGEsiYuE # KprRzXSyMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRY # MFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p # Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEF # BQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w # a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo # MSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI # hvcNAQELBQADggIBAK9gCxC4IVbYKVQBHP5ztJc/kfgSubcL5hTReVE1uwSVKp92 # Sfd/IIvFgGQcwVlAZc8DubOhTshlR2fSFfK6+sUzoMOuf9ItKF7m348+SpZ455iI # TDyTgEjqXhTmTTvBfyEHA6gxHGzVo578k2Qsc7qSuXmPr8ZkeuRNHNOxFRQmnUWm # dTOLGJlbJq9zTH+KYbnJZ2tK5xwT2d2irtBu7U/FruzCxSbnM00y6dpYZcMUCdLu # zxHEnX8/epO1nQlrpUTpJ6gel2Pv+E+4oktdX8zz0Y0WfwdQOZVbn5gr/wPLvIoc # eKJJ366AA36lbc8Do5h6TSvJbVArNutbg/1JcCT5Tl9peMEmiK1b3z5kRFZffztU # e9pNYnhijkGaQnRTbsBqXaCCLmPU9i4PEHcOyh8z7t5tzjOAnQYXi7oNBbRXitz8 # XbPK2XasNB9QaU+01TKZRlVtYlsWrDriN7xCwCcx4bUnyiHGNiV5reIsDMbCKZ7h # 1sxLIQeg5tW/Mg3R30EnzjFV5cq8RPXvoaFj89LpFMlmJbk8+KFmHzwXcl5wS+GV # y38VulA+36aEM4FADKqMjW10FCUEVVfznFZ3UlGdSS7GqyFeoXBzEqvwaIWxv0BX # vLtNPfR+YxOzeCaeiMVC3cx0PlDcz+AF/VN2WHKI81dOAmE/qLJkd/EpmLZzMIIH # cTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCB # iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMp # TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEw # OTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ # Q0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIh # C3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNx # WuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFc # UTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAc # nVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUo # veO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyzi # YrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9 # fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdH # GO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7X # KHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiE # R9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/ # eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3 # FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAd # BgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEE # AYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMI # MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMB # Af8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1Ud # HwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By # b2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQRO # MEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2Vy # dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4IC # AQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pk # bHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gng # ugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3 # lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHC # gRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6 # MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEU # BHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvsh # VGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+ # fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrp # NPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHI # qzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAs4wggI3AgEBMIH4 # oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUw # IwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1U # aGFsZXMgVFNTIEVTTjpBRTJDLUUzMkItMUFGQzElMCMGA1UEAxMcTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUA0PommlVZaduKtDHg # hztBZDfmVv6ggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN # BgkqhkiG9w0BAQUFAAIFAOXakoYwIhgPMjAyMjAzMTUxMjIwMjJaGA8yMDIyMDMx # NjEyMjAyMlowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA5dqShgIBADAKAgEAAgIj # ngIB/zAHAgEAAgIg1DAKAgUA5dvkBgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgor # BgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUA # A4GBAAUBKq1aV00iZ4U635sPOJ1ks/0vA49iEqmVq2IC6H0xqDHK9bKu3Teld3hb # Ozy5dIzNhYnhiD3hB3uDwBNg+PXnYzxbKLDYooDe9YEw/C5DBqnjdHchtSdGtJuB # nsT9WldzGVzkX/cmaD2MeErNfy7dNRsCEKcuwd3MUsHZtLz3MYIEDTCCBAkCAQEw # gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAGWSVti4S/d908A # AQAAAZYwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B # CRABBDAvBgkqhkiG9w0BCQQxIgQgCXzcRDOSokiKzAPbOQTwwwFplBIRbHSoNOyt # x9hLV88wgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCB2BNYC+B0105J2Ry6C # fnZ0JA8JflZQQ6sLpHI3LbK9kDCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAABlklbYuEv3fdPAAEAAAGWMCIEIL3oOQ86+RAzR5ktZAuT # O+qW1v2jtNCJYLWPOPMVhUC6MA0GCSqGSIb3DQEBCwUABIICAFuprUpDaswej2EI # 5mmSzHrq+nGQ5VoqrjejDpLBS1U4PUm0eLOo7FsHcvJfPIyjZ8y5KlcLYUFsF3DE # luXFUtx7HcApqfAN5Q/3qnsU1mnhD1PBo9Xggl4h9XAn5gE3DCAUgBJT4fktVWcG # MLhkQSDnS4QjOSdx6XcLiXmJNGnE7+33RaD4bK3EcIoIcoUXjVCL3yLNW0sPCkeO # amDr5h+Ra+GcIErImOCW7f3F+8iBWD95MM81TnGjeEvC567f3k7Y+1xm/xr211CW # m2bxptDQN19StgKZEb6MNqqzORO5nR64jBH7Wo+U88403TsF3w31gYBhMfEkemRZ # sqjHCY+yCIj3IzT3b6OH867ZkJa9YHlxz9CluiGSnpik1ala6BULmljVgFzzwapQ # ZQCrgbj2rSfUPzHqxnzcVTiurduUnfaKW4ssXPvErrDYHiYSKP57uvGL8rFNp1hb # ehy85tU50utxtFzJSBznY/LRQKzOTl18hPZ6UHsKYLEKkqDWFuU3/UUEhCaljISy # kMZfLQBI2/iICfiN1h6yK3VBFNoHEmChdYNKqOPf3uTdx0FCrFBzZ78OUhpy5tWX # 7F7MX4gOF2ssQRoindfqYMxDDhuhn9o0B0BAm2SHvK8XSywpssccoL6o0L7+68fQ # O5icMBg2TPaECsZXSRJ9e4/1ICR/ # SIG # End signature block |