targetgroupmgmt.ps1

# Setup APIs
function New-ICTargetGroup {
    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [String]$Name,

        [parameter(Mandatory=$false)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [String]$ControllerGroupId,

        [parameter(HelpMessage="Use first available ControllerGroupId if not provided.")]
        [Switch]$Force
    )

    $Endpoint = "targets"
    $body = @{
        name = $Name
    }

    if ($ControllerGroupId) {
        $body['controllerGroupId'] = $ControllerGroupId
    } else {
        $cg = Get-ICControllerGroup
        if ($cg.count -gt 1 -AND $Force) {
            $body['controllerGroupId'] = ($cg | Sort-Object createdOn -Desc)[0].id
        }
        elseif ($cg.count -gt 1) {
            Write-Error "More than one Controller Group. Recommend specifying a ControllerGroupId. Available Options: `n$($cg | Format-Table -auto | Out-String)"
            return
        } else {
            $body['controllerGroupId'] = $cg.id
        }
    }

    $tg = Get-ICTargetGroup -where @{ name = $Name }
    if ($tg) {
        Write-Error "There is already a Target Group named $Name"
    } else {
        Write-Verbose "Creating new target group: $Name"
        Invoke-ICAPI -Endpoint $Endpoint -body $body -method POST
    }
}

function Get-ICTargetGroup {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipeline=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('TargetGroupId','targetId')]
        [String]$Id,

        [Switch]$IncludeArchive,

        [parameter(HelpMessage="This will convert a hashtable into a JSON-encoded Loopback Where-filter: https://loopback.io/doc/en/lb2/Where-filter ")]
        [HashTable]$where=@{},
        [Switch]$NoLimit,
        [Switch]$CountOnly
    )

    PROCESS {
        if ($IncludeArchive) {
            $Endpoint = "TargetsArchive"
        } else {
            $Endpoint = "targets"
        }

        if ($Id) {
            $Endpoint += "/$Id"
        }

        Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly
    }
}

function Remove-ICTargetGroup {
    [cmdletbinding(SupportsShouldProcess=$true)]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('TargetGroupId','targetId')]
        [String]$Id,

        [Switch]$IncludeArchive
    )

    PROCESS {
        $obj = Get-ICTargetGroup -id $Id
        if (-NOT $obj) {
            Write-Error "No target group with id '$Id' exists."
            return
        }
        if ($IncludeArchive) {
            $Endpoint = "TargetsArchive/$Id"
        } else {
            $Endpoint = "targets/$Id"
        }

        Write-Warning "Careful. This will remove access to all scan data within this target group and is only reversible for the next 7 days"
        if ($PSCmdlet.ShouldProcess($tg.name, "Will remove target group: $($obj.name) [$Id]")) {
            Write-Warning "Removing target group: $($obj.name) [$Id]."
            Invoke-ICAPI -Endpoint $Endpoint -method 'DELETE'
        }
    }
}

function New-ICControllerGroup {
    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [String]$Name
    )

    $Endpoint = "controllergroups"
    $body = @{
        name = $Name;
    }
    $cg = Get-ICControllerGroup -where @{ name = $Name; deleted = $False }
    if ($cg) {
        Write-Error "There is already a Controller Group named $Name"
    } else {
        Write-Verbose "Creating new Controller Group: $Name [$HuntServerAddress/api/$Endpoint]"
        Invoke-ICAPI -Endpoint $Endpoint -body $body -method POST
    }
}

function Get-ICControllerGroup {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipeline=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('controllerGroupId')]
        [String]$Id,

        [parameter(HelpMessage="This will convert a hashtable into a JSON-encoded Loopback Where-filter: https://loopback.io/doc/en/lb2/Where-filter ")]
        [HashTable]$where=@{},
        [Switch]$NoLimit,
        [Switch]$CountOnly
    )

    PROCESS {
        $Endpoint = "controllergroups"
        if ($Id) {
            $Endpoint += "/$id"
        }
        Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly
    }
}

function Remove-ICControllerGroup {
    [cmdletbinding(SupportsShouldProcess=$true)]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('ControllerGroupId')]
        [String]$Id
    )

    PROCESS {
        $Endpoint = "controllergroups/$Id"
        $obj = Get-ICControllerGroup -id $Id
        if (-NOT $obj) {
            Write-Error "No Controller Group with id '$Id' exists."
            return
        }
        if ($PSCmdlet.ShouldProcess($obj.name, "Will remove target group: $($obj.name) [$Id]")) {
            Write-Warning "Removing Controller Group $Id."
            Invoke-ICAPI -Endpoint $Endpoint -method 'DELETE'
        }
    }
}

function New-ICCredential {
    [cmdletbinding()]
    Param(
        [parameter(Mandatory=$True)]
        [String]$Name,

        [parameter(Mandatory=$True)]
        [PSCredential]$Cred,

        [parameter()]
        [ValidateSet("windowsLocal","windowsDomain","aws","ssh","login","linuxSudo")]
        [String]$AccountType="login"
    )

    $Endpoint = "credentials"
    $body = @{
        name = $Name
        username = $Cred.Username
        password = $Cred.GetNetworkCredential().Password
        byok = $False
        login = $AccountType
    }
    $c = Get-ICCredential -where @{ name = $Name; deleted = $False }
    if ($c) {
        Write-Error "There is already a credential object named $Name"
    } else {
        Write-Verbose "Adding new Credential $Name [$($Cred.Username)] to the Credential Manager"
        Invoke-ICAPI -Endpoint $Endpoint -body $body -method POST
    }
}

function Get-ICCredential {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipeline=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('credentialId')]
        [String]$id,

        [parameter(HelpMessage="This will convert a hashtable into a JSON-encoded Loopback Where-filter: https://loopback.io/doc/en/lb2/Where-filter ")]
        [HashTable]$where=@{},
        
        
        [Switch]$NoLimit,
        [Switch]$CountOnly
    )

    PROCESS {
        $Endpoint = "credentials"
        if ($id) {
            $Endpoint += "/$id"
        }
        Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly
    }
}

function Remove-ICCredential {
    [cmdletbinding(SupportsShouldProcess=$true)]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [alias('credentialId')]
        [String]$Id
    )

    PROCESS {
        $Endpoint = "credentials/$Id"
        $obj = Get-ICCredential -id $Id
        if (-NOT $obj) {
            Write-Error "No target group with id '$Id' exists."
            return
        }
        if ($PSCmdlet.ShouldProcess($obj.name, "Will remove Credential Object: $($obj.name) [$Id]")) {
            Write-Warning "Removing Credential $($obj.name) [$Id]."
            Invoke-ICAPI -Endpoint $Endpoint -method 'DELETE'
        }
    }
}

function Get-ICAddress {
    [cmdletbinding()]
    param(
        [parameter()]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('addressId')]
        [String]$Id,

        [parameter(ValueFromPipeLine=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('targetId')]
        [String]$TargetGroupId,

        [parameter(HelpMessage="This will convert a hashtable into a JSON-encoded Loopback Where-filter: https://loopback.io/doc/en/lb2/Where-filter ")]
        [HashTable]$where=@{},
        
        [Switch]$NoLimit,
        [Switch]$CountOnly
    )

    PROCESS {
        $Endpoint = "Addresses"

        if ($Id) {
            Write-Verbose "Getting Address with id: $Id"
            $Endpoint += "/$Id"
        }
        elseif ($TargetGroupId) {
            Write-Verbose "Getting all Addresses from TargetGroup: $TargetGroupId"
            $where += @{ targetId = $TargetGroupId }
        }

        Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly
    }
}

function Remove-ICAddress {
    [cmdletbinding(SupportsShouldProcess=$true)]
    Param(
        [parameter(
            ValueFromPipelineByPropertyName,
            ValueFromPipeLine)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('AddressId')]
        [String]$id,

        [parameter(ValueFromPipelineByPropertyName=$true)]
        [alias('targetId')]
        [String]$TargetGroupId
    )

    PROCESS {
        $Endpoint = "Addresses"

        if ($Id) {
            $obj = Get-ICAddress -id $Id
            if (-NOT $obj) {
                Write-Error "No Address with id '$Id' exists."
                return
            }
            if ($PSCmdlet.ShouldProcess($obj.hostname, "Will remove Address: $($obj.hostname) [$Id]")) {
                Write-Warning "Removing Address $($obj.hostname) [$Id]."
                $Endpoint = "Addresses/$id"
            }
        }
        elseif ($TargetGroupId) {
            $obj = Get-ICTargetGroup -id $TargetGroupId
            if (-NOT $obj) {
                Write-Error "No Target Group with id '$TargetGroupId' exists."
                return
            }
            if ($PSCmdlet.ShouldProcess($obj.name , "Clearing ALL Addresses from Target Group: $($obj.name) [$TargetGroupId]")) {
                Write-Warning "Clearing all Addresses from TargetGroup: $($obj.name) [$TargetGroupId]"
                $body['where'] = @{ targetId = $TargetGroupId }
            }
        }
        else {
            Write-Error "Provide either an addressId or a targetGroupId."
            return
        }

        Invoke-ICAPI -Endpoint $Endpoint -body $body -method DELETE
    }
}

function Get-ICAgent {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipeline=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('agentId')]
        [String]$Id,

        [parameter(HelpMessage="This will convert a hashtable into a JSON-encoded Loopback Where-filter: https://loopback.io/doc/en/lb2/Where-filter ")]
        [HashTable]$where=@{},
        
        
        [Switch]$NoLimit,
        [Switch]$CountOnly
    )

    PROCESS {
        if ($Id) {
            $CountOnly = $False
            $Endpoint = "Agents/$Id"
        } else {
            $Endpoint = "Agents"
        }
        Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly
    }
}

function Remove-ICAgent {
    [cmdletbinding(SupportsShouldProcess=$true)]
    Param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('AgentId')]
        [String]$Id
    )

    PROCESS {
        $Endpoint = "agents/uninstall"
        $obj = Get-ICAgent -id $Id
        if (-NOT $obj) {
            Write-Error "No Agent exists with id: $Id"
            return
        }

        $body = @{
            where = @{ id = $Id }
        }

        if ($PSCmdlet.ShouldProcess($obj.hostname, "Will uninstall agent: $($obj.hostname) [$Id]")) {
            Write-Verbose "Uninstalling Agent $($obj.hostname) [$Id]."
            Invoke-ICAPI -Endpoint $Endpoint -body $body -method POST
            Write-Verbose "Uninstall pending for Agent on $($obj.hostname) [$Id]."
            return $true
        }
    }
}

function New-ICQuery {
    [cmdletbinding()]
    Param(
        [parameter(Mandatory=$True)]
        [ValidateNotNullorEmpty()]
        [alias('QueryName')]
        [String]$Name,

        [parameter(Mandatory=$True)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('targetId')]
        [String]$TargetGroupId,

        [parameter(Mandatory=$True)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [String]$credentialId,

        [parameter()]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [String]$sshCredentialId,

        [parameter(Mandatory=$True)]
        [ValidateNotNullorEmpty()]
        [String]$Query
    )

    $Credential = Get-ICCredential -Id $CredentialId
    if (-NOT $Credential) {
        Throw "Credential with CredentialId $CredentialId does not exist."
    }
    $TargetGroup = Get-ICTargetGroup -Id $TargetGroupId
    if (-NOT $TargetGroup) {
        Throw "Credential with TargetGroup $TargetGroupId does not exist."
    }
    Write-Verbose "Creating new Query $Name ($query) in TargetGroup $($TargetGroup.name) using credential $($Credential.name) [$($Credential.username)]"
    $Endpoint = "queries"
    $body = @{
        name = $Name
        value = $query
        targetId = $TargetGroupId
        credentialId = $CredentialId
    }
    if ($sshCredentialId) {
        $body['sshCredential'] = $sshCredentialId
    }
    Invoke-ICAPI -Endpoint $Endpoint -body $body -method POST
}

function Get-ICQuery {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipeline=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('queryId')]
        [String]$Id,

        [parameter(ValueFromPipelineByPropertyName=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('targetId')]
        [String]$TargetGroupId,

        [parameter(HelpMessage="This will convert a hashtable into a JSON-encoded Loopback Where-filter: https://loopback.io/doc/en/lb2/Where-filter ")]
        [HashTable]$where=@{},
        
        
        [Switch]$NoLimit,
        [Switch]$CountOnly
    )

    PROCESS {
        $Endpoint = "queries"
        if ($Id) {
            Write-Verbose "Getting Query: $Id"
            $Endpoint += "/$Id"
        }
        elseif ($TargetGroupId) {
            Write-Verbose "Getting Queries for Target Group Id: $TargetGroupId"
            $where += @{ targetId = $TargetGroupId }
        }
        Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$true -CountOnly:$CountOnly
    }
}

function Remove-ICQuery {
    [cmdletbinding(SupportsShouldProcess=$true)]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('queryId')]
        [String]$Id
    )

    PROCESS {
        $Endpoint = "queries/$Id"
        if ($PSCmdlet.ShouldProcess($Id, "Will remove this query")) {
            Write-Warning "Removing query $Id"
            Invoke-ICAPI -Endpoint $Endpoint -method 'DELETE'
        }
    }
}

# SIG # Begin signature block
# MIINFwYJKoZIhvcNAQcCoIINCDCCDQQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUr7Iaqc5wMaCIq3Z8BAAu5SED
# BS2gggpZMIIFITCCBAmgAwIBAgIQD1SHruUyzkN01AFx5d7oATANBgkqhkiG9w0B
# AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz
# c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMTExNzAwMDAwMFoXDTIyMTEy
# OTIzNTk1OVowXjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQH
# EwZBdXN0aW4xFjAUBgNVBAoTDUluZm9jeXRlLCBJbmMxFjAUBgNVBAMTDUluZm9j
# eXRlLCBJbmMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNUhaEiZbu
# H7Q6oqA0uq0klkstXkdJU3eJukMrrLpxKdtFqtJwFXcSYp5G/WFlwDRJ8v8fisfp
# SiPS1WajFUHe3EWLh2oXjf44eQYVWQ8SqAn2J8dDLNJ5bWY0w7MD2GrSiTwN0Vi9
# X9pJKJDdm7mJo7bSlZ9p7XvNoraSAx/hkODalPSMvCIVAEOZutlzeWyJ4p0DbTDA
# kjQPF4EZ7JqxYXFeItoi0uYZQNEHbBxr+5SG45ziC8vuwyljIbo+mKD/PwT48OQl
# 9cdnI651Hz+r5kL3t48WvxYrAUJ7g8EJyw2uYnVnSroIC3TmUQHeXS6FuqeObuX7
# MqapBKRcTHvxAgMBAAGjggHFMIIBwTAfBgNVHSMEGDAWgBRaxLl7KgqjpepxA8Bg
# +S32ZXUOWDAdBgNVHQ4EFgQUxKDwshqav/aGaGVOFv67CuwSjcYwDgYDVR0PAQH/
# BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGA1UdHwRwMG4wNaAzoDGGL2h0
# dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMDWg
# M6Axhi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcx
# LmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwDATAqMCgGCCsGAQUFBwIBFhxodHRw
# czovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAEEATCBhAYIKwYBBQUHAQEE
# eDB2MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTgYIKwYB
# BQUHMAKGQmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJB
# c3N1cmVkSURDb2RlU2lnbmluZ0NBLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3
# DQEBCwUAA4IBAQByJW5tIcmouIY7tdngPLdEOM4FYqLGf9IjKPMS0s+NeTaP/0hp
# dmNeGFEvMozfgDA/gPFCUaRVJwy4rKsGnCznCE1YDA6UFDGZq3VLUbzC6GDP4aY8
# EbfDMbF54TVuOKRue9a6KnVE67gOj+g862qAR6fm/GdeO/KrdvCT1A7xbyg02cCq
# +QgdkYoxI3bsiUwgZ33I2rn2T2zSp8C+RX2bZ8rgtXHxgYLCJdayqMptRsPbxOlQ
# Z7dRhkQXg5D/PyUnpWASF+sLQQ0IMvx8ZKy/P01IhKU0pTJ8OFSYKwPLQnYm1Zp0
# JT/IXZ/tzmtY/StdhaCs3LlOkuHxl2iERxdtMIIFMDCCBBigAwIBAgIQBAkYG1/V
# u2Z1U0O1b5VQCDANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UE
# ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD
# VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAw
# WhcNMjgxMDIyMTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdp
# Q2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG
# 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/
# 5aid2zLXcep2nQUut4/6kkPApfmJ1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH
# 03sjlOSRI5aQd4L5oYQjZhJUM1B0sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxK
# hwjfDPXiTWAYvqrEsq5wMWYzcT6scKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr
# /mzLfnQ5Ng2Q7+S1TqSp6moKq4TzrGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi
# 6CxR93O8vYWxYoNzQYIH5DiLanMg0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCC
# AckwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAww
# CgYIKwYBBQUHAwMweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8v
# b2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6
# MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
# ZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1s
# AAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMw
# CgYIYIZIAYb9bAMwHQYDVR0OBBYEFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1Ud
# IwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+
# 7A1aJLPzItEVyCx8JSl2qB1dHC06GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbR
# knUPUbRupY5a4l4kgU4QpO4/cY5jDhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7
# uq+1UcKNJK4kxscnKqEpKBo6cSgCPC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7
# qPjFEmifz0DLQESlE/DmZAwlCEIysjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPa
# s7CM1ekN3fYBIM6ZMWM9CBoYs4GbT8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR
# 6mhsRDKyZqHnGKSaZFHvMYICKDCCAiQCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTAT
# BgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEx
# MC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBD
# QQIQD1SHruUyzkN01AFx5d7oATAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEK
# MAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3
# AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUb89UFwbd3PVf00Zr
# n8/ib1Dvxr8wDQYJKoZIhvcNAQEBBQAEggEAdkP+OHCyUM153Cm1/1L7pkMZKueA
# 4XKnAHlF/wlAZYpm1gup2Lb0tr8ZdLNh7Jz7NNqGIwn5rd4QEajEuuIhSFTP4BK5
# aRV7jtVShjrjLnM5JXDRguhXpO+Y0oZ8olEtaFqEEZLlvIlYruw72zFo1JGn7BsL
# oQX24MtpNWSyVrkVJgAhpkc1vCvhpDdwtqi1IsmkbKew6sS8awprpnbMnaGGZFd8
# zroz1a75Se7c5GzwSmWkmxgYAMaplVSD2XIHut9OEoY68AXrIBsStiasiQkqj+5L
# RNn7pGJAzbyyA+aG3/B1aUOW7hiEObPTAD31/uOsdqtO1hF7yZ27vvh0vw==
# SIG # End signature block