status.ps1


# Status and Progress APIs

# Get Infocyte HUNT Jobs (Active jobs or all jobs)
function Get-ICJob {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipelineByPropertyName)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('jobId')]
        [String]$Id,

        [Switch]$All,

        [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 = 'jobs'
        if ($Id) {
            $CountOnly = $false
            $endpoint += "/$Id"
            $where = $null
            $NoLimit = $false
        } else {
            if ($All) {
                Write-Verbose "Getting All Jobs."
            } else {
                Write-Verbose "Getting Active Jobs."
                if (-NOT $where['state']) {
                    $where['state'] = "active"
                }
            }
        }
        Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly
    }
}

# Get Infocyte HUNT User Audit Logs
function Get-ICAuditLog {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipeline)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [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
    )
    BEGIN {
        $users = Get-ICAPI -endpoint users -nolimit
    }

    PROCESS {
        $endpoint = 'useractivities'
        if ($Id) {
            $CountOnly = $false
            $endpoint += "/$Id"
        }
        Write-Verbose "Getting User Activity Logs"
        if ($CountOnly) {
            Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly
        } else {
            Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly | foreach-object {
                $userid = $_.userId
                $user = $users | where { $_.id -eq $userid }
                $_ | Add-Member -Type NoteProperty -Name username -Value $user.username
                $_.query = $_.query | ConvertTo-Json -Depth 10 -Compress
                $_
            }
        }
    }
}

# Get Infocyte HUNT User Tasks. These are the items in the task dropdown in the UI.
function Get-ICTask {
    [cmdletbinding()]
    param(
        [parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [ValidateScript({ if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid."} })]
        [alias('id')]
        [alias('taskId')]
        [String]$userTaskId,

        [Switch]$All,

        [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 = "usertasks"
        if ($userTaskId) {
            $CountOnly = $false
            $endpoint += "/$userTaskId"
        } else {
            if (-NOT $All -AND $where.keys.count -eq 0) {
                Write-Verbose "Filtering for running and recently ended tasks (Default)."
                $where = @{ and = @() }
                $where['and'] += @{ type = @{ neq = "RTS"} }
                $where['and'] += @{ or = @( 
                        @{ status = "Active" },
                        @{ endedOn = @{ gte = (Get-Date).ToUniversalTime().AddDays(-1).ToString() } }
                    )}
                $where['and'] += @{ archived = $false }
                Write-Verbose "Using filter: $($Where | convertto-json)"
            }
        }
        $Tasks = Get-ICAPI -Endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly
        $Tasks | ForEach-Object {
            $_ | Add-Member -Type NoteProperty -Name totalSeconds -Value $null
            if ($_.endedOn) {
                $_.totalSeconds = [math]::round(([datetime]$_.endedOn - [DateTime]$_.createdOn).TotalSeconds)
            }
        }
        Write-Output $Tasks
    }
}

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

        [parameter()]
        [Switch]$IncludeProgress,

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

    PROCESS {
        $Endpoint = "userTaskItems"
        $where['userTaskId'] = $userTaskId
        
        if ($CountOnly) {
            return (Get-ICAPI -Endpoint $Endpoint -where $where -fields $fields -NoLimit:$NoLimit -CountOnly:$CountOnly)
        }

        Write-Verbose "Getting All TaskItems with TaskId $userTaskId."
        $Items = Get-ICAPI -Endpoint $Endpoint -where $where -fields $fields -NoLimit:$NoLimit -CountOnly:$CountOnly
        ForEach ($item in $items) {
            $item | Add-Member -Type NoteProperty -Name totalSeconds -Value $null
            if ($item.endedOn) {
                $item.totalSeconds = [math]::round(([datetime]$item.endedOn - [DateTime]$item.createdOn).TotalSeconds)
            }

            if ($item.type -eq "host-access" -OR $item.type -eq "enumeration") {
                $type = $item.type
                $item | Add-Member -Type NoteProperty -Name queryId -Value $null
                $item | Add-Member -Type NoteProperty -Name queryName -Value $null
                
                if ($type -eq "enumeration") {
                    $item.queryId = $item.result.queryId
                } 
                elseif ($type -eq "host-access") {
                    Write-Verbose "Getting QueryId from job"
                    try { $j = Get-ICJob -Id $item.jobId } catch { }
                    $item.queryId = $j.data.queryId
                }

                if ($item.queryId) {
                    Write-Verbose "Getting QueryName"
                    $q = Get-ICQuery -Id $item.queryId
                    $item.queryName = $q.Name
                }
            }
        }

        if (-NOT $IncludeProgress) {
             
            Write-Output $Items

        } else {
            $n = 0
            if ($Id) {
                $cnt = 1
            } else {
                $cnt = Get-ICAPI -Endpoint $Endpoint -where $where -CountOnly
            }
            Write-Verbose "Found $cnt TaskItems. Getting progress for each."
            ForEach ($item in $items) {
                $item | Add-Member -Type NoteProperty -Name lastMessage -Value $null
                if ($item.type -eq "host-access") {
                    $item | Add-Member -Type NoteProperty -Name accessible -Value $null
                }

                $n += 1
                try { $pc = [math]::floor($n*100/$cnt); if ($pc -gt 100) { $pc = 100 } } catch { $pc = -1 }
                if ($item.id) {
                    Write-Progress -Id 101 -Activity "Enriching with Task Progress Information" -status "Getting progress on $($item.name) [$n of $cnt]" -PercentComplete $pc
                    $progresstext = @()
                    _Get-ICTaskItemProgress -taskItemId $item.id | ForEach-Object {
                        $p = $_
                        $p | foreach-object {
                            $progresstext += "$($_.createdOn) - $($_.text)"    
                            if (-NOT $LastProgressMsg) {
                                $LastProgressMsg = "$($_.createdOn) - $($_.text)"
                            }
                            if ($type -eq "host-access" -AND $_.text -match "^ACCESSIBLE:") { 
                                $item.accessible = $true
                                $item.lastMessage = $_.text
                            }
                            elseif ($type -eq "host-access" -AND $_.text -match "^INACCESSIBLE:") { 
                                $item.accessible = $false
                                $item.lastMessage = $_.text
                            }                                                                
                        }
                    }
                    if ($item.status -ne "complete" -AND -NOT $item.message) {
                        $item.lastMessage = $LastProgressMsg
                    }
                    $item | Add-Member -MemberType "NoteProperty" -name "progress" -value ([string[]]$progresstext)
                }
            }
            Write-Output $items
        }
    }
}

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

        [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
    )

    PROCESS {
        $Endpoint = "userTaskItemProgresses"
        if ($_.id -AND $_.taskItemId) {
            # disambuguation
            $where['taskItemId'] = $_.taskItemId
        } else {
            $where['taskItemId'] = $taskItemId
        }
        Get-ICAPI -Endpoint $Endpoint -where $where -fields @("createdOn", "text") -NoLimit:$NoLimit | Sort-Object createdOn -Descending
    }
}

function Wait-ICTask {
    [cmdletbinding()]
    param(
        [parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName)]
        [ValidateScript( { if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid." } })]
        [alias('id')]
        [alias('userTaskId')]
        [String]$TaskId
    )

    BEGIN {}
    PROCESS {
        $Task = Get-ICTaskItems -userTaskId $TaskId -IncludeProgress
        while (-NOT $Task.complete) {
            Start-Sleep 20
            $Task = Get-ICTaskItems -userTaskId $TaskId -IncludeProgress
        }
        return $true
    }
    END{}
}

function Get-ICLastScanTask {
    [cmdletbinding()]
    param(
        [parameter(Mandatory, Position=0)]
        [ValidateSet("Scan", "Enumerate")]
        [String]$Type,

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

    if ($targetGroupId) {
        $Task = Get-ICTask -where @{ targetGroupId = $targetGroupId; type = $Type } | Select-Object -Last 1
        if (-NOT $Task) {
            Write-Error "No $Type task was found within target group with Id: $targetGroupId"
            return
        }
    }
    else {
        $where = @{ and = @()}
        #$where['and'] += @{ endedOn = @{ gte = (Get-Date).ToUniversalTime().AddDays(-1).ToString() } }
        $where['and'] += @{ type = $Type; }
        $where['and'] += @{ archived = $false }
        $Task = Get-ICTask -where $where  | Select-Object -Last 1
        if (-NOT $Task) {
            Write-Error "No task was found with type: $Type"
            return
        }
    }

    $Progress = Get-ICTaskItems -userTaskId $Task.id -IncludeProgress -NoLimit

    $result = @{
        userTaskId = $Task.Id
        name = $Task.name
        createdOn = $Task.createdOn
        endedOn = $Task.endedOn
        totalSeconds = $task.totalSeconds
        status = $Task.status
        type = $Task.type
        accessibleCount     = ([Array]($Progress | Where-Object { $_.Accessible })).count
        inaccessibleCount     = ([Array]($Progress | Where-Object { -NOT $_.Accessible })).count
        totalItems            = ([Array]$Progress).count
        items = $Progress
    }
    $result['coverage'] = try { [math]::Round(($($result.accessibleCount)/$($result.totalItems)), 2) } catch { $null }
    return [PSCustomObject]$result
}
# SIG # Begin signature block
# MIINFwYJKoZIhvcNAQcCoIINCDCCDQQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUYTAXZgQOloABTzYApT8fAqeG
# gXSgggpZMIIFITCCBAmgAwIBAgIQD1SHruUyzkN01AFx5d7oATANBgkqhkiG9w0B
# 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
# AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUQGBjWnW4lld9u+h3
# L8ecVRo+HZgwDQYJKoZIhvcNAQEBBQAEggEAVcoFUwZ/5lkUh8ifwk3n2w2K61EU
# /vz6x1rGleAfy0qO6OFcboux5zdqWbOGkU6/kc6ZzVFFkDadxm/gkgdHC2F7AFIq
# cLVZ1c+i5k1Buv3MrojUqHB54FlZyW7oqlrRQdvsIzfu13mIZ2z/BpEPMS/qqthn
# ye4vrtXMlqJwj5EqzoX1rBvXlTdJ8HgDvlcg9pdc+FYPJ9YdpmyYJ2eKcsWcG4ap
# vmyRMo7uxfc1MjxLdzYPNP6wVJ7rLNOYqgrNgfvmJWZwilQRpaLKPqG//DmkQa/y
# 6UM/bdG8/n/ql6LjzHx1voi4QGO/CjMmyikZUAUrSZEo22VqvHgROWhLNQ==
# SIG # End signature block