rules.ps1


function Import-ICRule {
    [cmdletbinding(DefaultParameterSetName = 'Rule')]
    Param(
        [parameter(
            Mandatory, 
            ValueFromPipeline,
            ParameterSetName = 'Rule')]
        [ValidateNotNullOrEmpty()]
        [PSCustomObject]$Rule,

        [parameter(
            Mandatory, 
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            ParameterSetName = 'Path')]
        [ValidateNotNullorEmpty()]
        [String]$Path,

        [parameter(ParameterSetName = 'Rule')]
        [parameter(ParameterSetName = 'Path')]
        [Switch]$Active,

        [parameter(ParameterSetName = 'Rule')]
        [parameter(ParameterSetName = 'Path')]
        [Switch]$Force
    )

    PROCESS { 
        if ($Path) {
            $Body = Get-Content $Path -Raw
            #$reader = New-Object -TypeName System.IO.StreamReader -ArgumentList $Path
            $rules = ConvertFrom-Yaml $Body | convertto-json | convertfrom-json | ForEach-Object {
                if ($null -eq $_.rule) {
                    Write-Error "Incorrect filetype. Not an Infocyte Rule"
                    continue
                }
                $_
            }
        } else {
            if ($null -eq $rule.rule) {
                Write-Error "Incorrect filetype. Not an Infocyte Rule"
                continue
            }
            $rules = $rule
        }
        

        $rules | Foreach-Object {
            $rule = $_
            if ($rule.guid) {

                if ($rule.guid -notmatch $GUID_REGEX) {
                    Write-Error "Incorrect guid format: $($rule.guid). Should be a guid of form: $GUID_REGEX.
                    Use the following command to generate a new one: [guid]::NewGuid().Guid"

                    $guid = [guid]::NewGuid().Guid
                    Write-Warning "Missing guid: Generating a new one prior to import: $guid" 
                    $rule.guid = $guid
                } 
                else {
                    $existingRule = Get-ICRule -where @{ guid = $rule.guid } -NoBody | Select-Object -First 1
                    if ($existingRule) {
                        if (-NOT $Force) {
                            Write-Warning "There is already an existing rule named $($existingRule.name) [$($existingRule.Id)] with guid $($rule.guid). Try using Update-ICRule or use -Force flag."
                            continue
                        }
                        Write-Warning "There is already an existing rule named $($existingRule.name) [$($existingRule.Id)] with guid $($rule.guid). Forcing update."
                        $id = $existingRule.id
                        Invoke-ICAPI -Endpoint rules -method POST -body @{
                            id          = $id
                            name        = $rule.name
                            short       = $rule.short
                            description = $rule.description
                            body        = $rule.rule
                        }
                        continue
                    }
                }            
            }
            else {
                $rule | Add-Member -TypeName NoteProperty -Name guid -Value ([guid]::NewGuid().Guid)
            }

            Write-Verbose "Adding new Rule named: $($rule.name)"
            Invoke-ICAPI -Endpoint rule -method POST -body @{ 
                name        = $rule.name
                short       = $rule.short
                description = $rule.description
                body        = $rule.rule
                guid        = $rule.guid
            }
        }
    }
}

function Get-ICRule {
    [cmdletbinding(DefaultParameterSetName = "List")]
    Param(
        [parameter(
            Mandatory, 
            ValueFromPipeline,
            ParameterSetName = 'Id')]
        [ValidateScript( { if ($_ -match $GUID_REGEX) { $true } else { throw "Incorrect input: $_. Should be a guid." } })]
        [alias('ruleId')]
        [String]$Id,
        
        [parameter(
            ParameterSetName = 'List',
            HelpMessage = "This will convert a hashtable into a JSON-encoded Loopback Where-filter: https://loopback.io/doc/en/lb2/Where-filter ")]
        [HashTable]$where = @{},

        [parameter(
            ParameterSetName = 'List')]
        [Switch]$NoLimit,

        [parameter(
            ParameterSetName = 'List')]
        [Switch]$CountOnly,

        [Switch]$NoBody,

        [Switch]$Export
    )

    PROCESS {

        if ($Id) {
            Write-Verbose "Looking up rule by Id."
            $Endpoint = "rules/$Id"
            $rules = Get-ICAPI -Endpoint $Endpoint -ea 0
            if (-NOT $rules) {
                Write-Warning "Could not find rule with Id: $($Id)"
                return
            }
        }
        else {
            Write-Verbose "Getting rules"
            $Endpoint = "rules"
            $rules = Get-ICAPI -endpoint $Endpoint -where $where -NoLimit:$NoLimit -CountOnly:$CountOnly
            if (-NOT $rules) {
                Write-Verbose "Could not find any rules loaded with filter: $($where|convertto-json -Compress)"
                return
            }
            if ($CountOnly) { 
                return $rules 
            }
        }

        $n = 1
        if ($null -eq $rules.count) {
            $c = 1
        }
        else { 
            $c = $rules.count 
        }
        if (-NOT $NoBody -or $Export) {
            $rules | ForEach-Object {
                $rule = $_
                Write-Verbose "Getting Rule $($rule.name) [$($rule.id)]"
                try { $pc = [math]::Floor(($n / $c) * 100) } catch { $pc = -1 }
                Write-Progress -Id 1 -Activity "Getting Rule Body from Infocyte API" -Status "Requesting Body from Rule $n of $c" -PercentComplete $pc
                $ruleBody = Get-ICAPI -endpoint "rules/$($rule.id)/LatestVersion" -fields body, sha256
                $rule | Add-Member -MemberType NoteProperty -Name rule -Value $ruleBody.body 
                $rule | Add-Member -MemberType NoteProperty -Name sha256 -Value $ruleBody.sha256
                Write-Verbose "Looking up user: $($rule.createdBy) and $($rule.updatedBy)"
                $rule.createdBy = (Get-ICAPI -endpoint users -where @{ id = $rule.createdBy } -fields email -ea 0).email
                $rule.updatedBy = (Get-ICAPI -endpoint users -where @{ id = $rule.updatedBy } -fields email -ea 0).email
                $n += 1
            }  
            Write-Progress -Id 1 -Activity "Getting Rules from Infocyte API" -Status "Complete" -Completed
        }
        
        if ($Export) {
            $rules | ForEach-Object {
                $FilePath = "$($(Resolve-Path .\).Path)\$($($_.name).ToLower().Replace(' ','_')).yaml"
                $new_rule = [PSCustomObject][Ordered]@{
                    name = $_.name
                    guid = if ($_.guid) { $_.guid } else { $null }
                    rule = $_.body
                    author = $_.createdBy
                    description = $_.description
                    short = $_.short
                    severity = if ($_.severity) { $_.severity } else { "Medium" }
                    created = $_.created
                    updated = $_.updated
                    action = @{ alert = $true }
                }
                Write-Verbose "Exported Rule [$($_.name)] to $FilePath"
                $_ | Convertto-YAML -Force -OutFile $FilePath
            }
        }
        $rules
    }
}
function New-ICRule {
    [cmdletbinding()]
    param(
        [parameter(mandatory)]
        [String]$Name,
        
        [Parameter(Mandatory)]
        [String]$Rule,

        [Parameter()]
        [String]$Author = $env:Username,

        [Parameter()]
        [String]$Short,

        [Parameter()]
        [String]$Description,

        [Parameter()]
        [ValidateSet(
            "Critical",
            "High",
            "Medium",
            "Low"
        )]
        [String]$Severity = "Medium",
        
        [Switch]$Force
    )
    
    $today = Get-Date -UFormat "%F"
    
    $new_rule = [PSCustomObject][Ordered]@{
        name = $Name
        guid = [Guid]::NewGuid().guid
        rule = $Rule
        author = $Author
        description = $Description
        short = $Short
        severity = $Severity
        created = $today
        updated = $today
        action = @{ alert = $true }
    }

    $SavePath = (Resolve-Path .\).Path + "\new_rule.yaml"
    Write-Verbose "Created rule from template and saved to $SavePath"
    $new_rule | ConvertTo-YAML -OutFile $SavePath -Force
    Write-Output $new_rule
}
function Update-ICRule {
    <#
        Updates an existing rule with a new body from a file or string.
    #>

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

        [parameter(Mandatory)]
        [ValidateNotNullorEmpty()]
        [PSCustomObject]$Rule,

        [parameter(Mandatory)]
        [Switch]$Active
    )
    
    PROCESS {
        $Endpoint = "rules"

        $rule = ConvertFrom-Yaml $Body | convertto-json | convertfrom-json
        if ($null -eq $_.rule) {
            Write-Error "Incorrect filetype. Not an Infocyte Rule"
            return
        }

        $postbody = @{ 
            name        = $rule.name
            short       = $rule.short
            description = $rule.description
            body        = $rule.rule
            guid        = $rule.guid
            active      = $Active
        }

        if ($Id) {
            Write-Verbose "Looking up rule by Id"
            $existing_rule = Get-ICRule -id $Id -ea 0
            if ($existing_rule) {
                $Endpoint = "rules/$Id"
                Write-Verbose "Rule found: `n$($existing_rule | ConvertTo-Json)"
                if (-NOT $postbody['active']) { $postbody['active'] = $existing_rule.active }
                if (-NOT $postbody['name']) { $postbody['name'] = $existing_rule.name }
                if (-NOT $postbody['short']) { $postbody['short'] = $existing_rule.short }
                if (-NOT $postbody['description']) { $postbody['description'] = $existing_rule.description }
                if (-NOT $postbody['body']) { $postbody['body'] = $existing_rule.body }

                if (-NOT $postbody['guid']) { $postbody['guid'] = $existing_rule.guid }
                elseif ($rule.guid -AND $existing_rule.guid -ne $rule.guid) {
                    Write-Warning "Rule guids do not match. Cannot be updated, try importing the new rule!`nCurrent: $($existing_rule.guid)`nNew: $($rule.guid)"
                    return
                }
            } else {
                Write-Warning "Rule with id $id not found!"
                return
            }
        }
        else {
            Write-Verbose "Looking up rule by Guid"
            $existing_rule = Get-ICRule -ea 0 -where @{ guid = $rule.guid } -NoBody
            if ($existing_rule) {
                Write-Verbose "Found existing rule with matching guid $($existing_rule.guid). Updating id $($existing_rule.id)"
                $Endpoint = "rules/$($existing_rule.id)"
                $existing_rule = Get-ICRule -id $existing_rule.id
                if (-NOT $postbody['active']) { $postbody['active'] = $existing_rule.active }
                if (-NOT $postbody['name']) { $postbody['name'] = $existing_rule.name }
                if (-NOT $postbody['short']) { $postbody['short'] = $existing_rule.short }
                if (-NOT $postbody['description']) { $postbody['description'] = $existing_rule.description }
                if (-NOT $postbody['body']) { $postbody['body'] = $existing_rule.body }
            } 
            else {
                Write-Warning "Could not find existing rule with Guid: $($existing_rule.guid)"
                return
            }
        }

        Write-Verbose "Updating Rule: $($rule['name']) [Guid=$($rule.guid)] with `n$($postbody|convertto-json)"
        if ($PSCmdlet.ShouldProcess($($rule.name), "Will update rule $($postbody['name']) [$postbody['id'])]")) {
            Invoke-ICAPI -Endpoint $Endpoint -body $postbody -method POST
        }
    }
}

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

    PROCESS {
        if ($Id) {
            $rule = Get-ICRule -id $Id
            if (-NOT $rule) {
                Write-Error "Rule with id $id not found!"
                return
            }
    
            $Endpoint = "rules/$Id"
            if ($PSCmdlet.ShouldProcess($($rule.Id), "Will remove $($rule.name) with ruleId '$($rule.id)'")) {
                try {
                    Invoke-ICAPI -Endpoint $Endpoint -Method DELETE
                    Write-Verbose "Removed rule $($rule.name) [$($rule.id)]"
                } catch {
                    Write-Warning "Rule $($rule.name) [$($rule.id)] could not be removed!"
                }
            }
        } else {
            $endpoint = "rules"
            Invoke-ICAPI -Endpoint $Endpoint -Method DELETE
            Write-Verbose "Removed all rules"
        }
    }
}

# SIG # Begin signature block
# MIINFwYJKoZIhvcNAQcCoIINCDCCDQQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUBHi0mwHsg0CKKRMEd1t7E2oG
# 5rigggpZMIIFITCCBAmgAwIBAgIQD1SHruUyzkN01AFx5d7oATANBgkqhkiG9w0B
# 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
# AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUrMr91Uc/MA3JTX7d
# NeCNUMWMCpswDQYJKoZIhvcNAQEBBQAEggEALDqnoxCkKTkvzbLHME9Mw6lIzQ43
# wYxSiFteJ1BxGuzl4SR1f7tFKadtwyKb4oVQmbN2EJHCup7+ZWW1Ki7cNJ8mZbK6
# xQGpWyz35BB4dWh25v+LmIaS9621naEXJ/8Sg2xmXUXZzv0nLu2kHWEY/WjMPIkY
# xTrn2D4xQyOaQvgBKzWr6fGObtrV6R/NN8uwjh6VgG2jStjUg2/VQV/rEU4UNnUZ
# GHKmp6R0kZM+yq68nAz/bRO+YEZ1WQtpUNFW7R44Te1+IVY5a07Jg1tznw/1B60k
# TvPL7mUmIUVPvIvgL/Q8vauxHZO8L49BrUZcibO8UhOyg/Th+h4CsjgQhQ==
# SIG # End signature block