GuardiCore-config.psm1

# Converts UUIDs of labels to a key:value string for UUID-agnostic configs
# Importantly, this ignores asset IDs, as assets are still defined by ID
function ConvertFrom-LabelID {
    param (
        [Parameter(Mandatory,Position = 0)]
        $JSON,

        [PSTypeName("GCApiKey")]
        $ApiKey
    )

    # The below loops expect an array as input; passing the whole string will make all the label names the same
    if ( $JSON.GetType().Name -eq "String" ) {
        $JSON = $JSON.Split("`n")
    }

    $LabelCount = Get-GCLabel -Raw -ApiKey $ApiKey | Select-Object -ExpandProperty total_count
    $Labels = Get-GCLabel -Limit $LabelCount -ApiKey $ApiKey

    $LabelTable = @{}
    foreach ( $Item in $Labels ) {
        $Name = $Item.key + ":" + $Item.value
        $LabelTable.Add($Item.id,$Name)
    }

    $Regex = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
    $NewContent = foreach ($Item in $JSON) {
        if ( $Item -match $Regex ) {
            $ID = $matches[0]
    
            if ( $LabelTable.ContainsKey($ID) ) {
                $Item -replace ($Regex,$LabelTable[$ID])
            } else {
                $Item
            }
        } else {
            $Item
        }
    }

    # Converts back to a string
    $NewContent -join "`n"
}

function Export-Labels {
    param (
        [PSTypeName("GCApiKey")]
        $ApiKey
    )

    if ( $ApiKey ) {
        $Key = $ApiKey
    } else {
        $Key = $Global:GCApiKey
    }

    $Token = $Key.Token
    $LabelCount = Get-GCLabel -ApiKey $ApiKey -Raw | Select-Object -ExpandProperty total_count

    $Labels = Get-GCLabel -ApiKey $ApiKey -Limit $LabelCount -DynamicCriteriaLimit 1000

    # Filtering out vcenter labels because it breaks other stuff
    $Labels = $Labels | Where-Object {$_.key -notmatch "vCenter"}

    $ParsedLabels = foreach ( $Label in $Labels ) {
        $Label.dynamic_criteria = @($Label.dynamic_criteria | Select-Object -Property argument,field,op)

        $AssetCount = Get-GCAsset -Label $Label -Raw | Select-Object -ExpandProperty total_count
        $Assets = Get-GCAsset -Label $Label -Limit ($AssetCount+1)

        $Label.added_assets = foreach ( $Asset in $Assets ) {
            $AssetLabels = Invoke-RestMethod -Method Post -Uri ($Key.Uri + "/visibility/labels/assets") -Body (@{asset_ids = @($Asset.id)} | ConvertTo-Json) -ContentType application/json -Headers @{Authorization = "bearer $Token"}
            $AssetLabel = $AssetLabels | Where-Object {($_.key -eq $Label.key) -and ($_.value -eq $Label.value)}

            if ($AssetLabel.is_static -eq $true) {
                $Asset | Select-Object name,_id
            }
        }
        if ( $Label.added_assets.count -eq 0 ) {
            $Label.added_assets = @()
        } else {
            $Label.added_assets = @($Label.added_assets)
        }

        $Label | Select-Object key,value,added_assets,dynamic_criteria
    }

    $ParsedLabels | ConvertTo-Json -Depth 99
}

function Export-Policy {
    param (
        [PSTypeName("GCApiKey")]
        $ApiKey
    )

    try {
        $PolicyCount = Get-GCPolicy -Section "allow","alert","block","override_allow","override_alert","override_block" -Raw -ApiKey $ApiKey | Select-Object -ExpandProperty total_count
        $Policy = Get-GCPolicy -Section "allow","alert","block","override_allow","override_alert","override_block" -Limit $PolicyCount -ApiKey $ApiKey
    }
    catch {
        $PolicyCount = Get-GCPolicy -Section "allow","alert","block","override" -Raw -ApiKey $ApiKey | Select-Object -ExpandProperty total_count
        $Policy = Get-GCPolicy -Section "allow","alert","block","override" -Limit $PolicyCount -ApiKey $ApiKey
    }


    $PublishedPolicy = foreach ( $ThisPolicy in $Policy ) {
        $Output = $ThisPolicy.published_version | Select-Object -ExcludeProperty *id 
        $Output | Add-Member -MemberType NoteProperty -Name section -Value $ThisPolicy.section_position
        $Output
    }

    $PolicyJson = $PublishedPolicy | ConvertTo-Json -Depth 99

    Convert-LabelID -JSON $PolicyJson -ApiKey $ApiKey
}

function Test-Labels {
    param (
        [Parameter(Mandatory,Position = 0)]
        $JSON,

        [PSTypeName("GCApiKey")]
        $ApiKey,

        [switch]
        $PassThru
    )

    if ( $ApiKey ) {
        $Key = $ApiKey
    } else {
        $Key = $Global:GCApiKey
    }

    $Test = try {
        Get-GCAsset -Raw -ApiKey $ApiKey
    }
    catch {
        if ( $_.Exception -match "403" ) {
            throw "Authentication error. Please check your API key."
        }
    }

    # Converts to a single string for ConvertFrom-Json
    if ( $JSON.gettype().BaseType -eq "System.Array" ) {
        $JSON = $JSON -join "`n"
    }

    $ShouldLabels = $JSON | ConvertFrom-Json

    Write-Host Label check

    $Results = foreach ( $Label in $ShouldLabels ) {
        $Result = [PSCustomObject]@{
            Label = $Label.Key + ": " + $Label.Value
            Criteria = $true
            "Added assets" = $true
            Pass = $true
        }

        Write-Host

        $CurrentLabel = Get-GClabel -LabelKey $Label.key -LabelValue $Label.value

        Write-Host $Result.Label -NoNewLine

        if ( -not $CurrentLabel ) {
            Write-Host " does not exist" -ForegroundColor Red
            $Result.Pass = $false
            continue
        } else {
            Write-Host " exists" -ForegroundColor Green
        }

        $CriteriaCount = $Label.dynamic_criteria.count
        for ( $i = 0; $i -lt $CriteriaCount; $i++ ) {
            $Criteria = $Label.dynamic_criteria[$i]

            $ItDescription = "dynamic criteria '" + $Criteria.field.tolower() + " " + $Criteria.op.tolower() + " " + $Criteria.argument.tolower() + "': "

            Write-Host $ItDescription -NoNewLine

            $CurrentCriteria = $CurrentLabel.dynamic_criteria[$i]

            $Should = @($Criteria.field,$Criteria.op,$Criteria.argument) -join ","
            $Current = @($CurrentCriteria.field,$CurrentCriteria.op,$CurrentCriteria.argument) -join ","

            if ( $Current -eq $Should ) {
                Write-Host pass -ForegroundColor Green
            } else {
                Write-Host fail -ForegroundColor Red
                $Result.Pass = $false
                $Result."Dynamic Criteria" = $false
            }
        }

        $TempCount = Get-GCAsset -Label $CurrentLabel -Raw -ApiKey $ApiKey | Select-Object -ExpandProperty total_count

        if ($TempCount -eq 0) {
            Continue
        }

        $CurrentLabelAssets = Get-GCAsset -Label $CurrentLabel -Limit $TempCount -ApiKey $ApiKey
        $CurrentLabel.added_assets = foreach ( $Asset in $CurrentLabelAssets ) {
            $Token = $Key.token
            $AssetLabels = Invoke-RestMethod -Method Post -Uri ($Key.Uri + "/visibility/labels/assets") -Body (@{asset_ids = @($Asset.id)} | ConvertTo-Json) -ContentType application/json -Headers @{Authorization = "bearer $Token"}
            $AssetLabel = $AssetLabels | Where-Object {($_.key -eq $Label.key) -and ($_.value -eq $Label.value)}

            if ($AssetLabel.is_static -eq $true) {
                $Asset | Select-Object name,_id
            }
        }

        if ( $CurrentLabel.added_assets.count -eq 0 ) {
            $CurrentLabel.added_assets = @()
        } else {
            $CurrentLabel.added_assets = @($CurrentLabel.added_assets)
        }

        $AssetCount = $Label.added_assets.count
        $Assets = $Label.added_assets | Sort-Object -Property name
        $CurrentAssets = $CurrentLabel.added_assets | Sort-Object -Property name
        for ( $i = 0; $i -lt $AssetCount; $i++ ) {
            $Asset = $Assets[$i]
            $ItDescription = "manually added asset '" + $Asset.name + " (" + $Asset._id + ")': "

            Write-Host $ItDescription -NoNewLine

            $CurrentAsset = $CurrentAssets[$i]
            $CurrentAssetRemote = Get-GCAsset -Asset $CurrentAsset -ApiKey $ApiKey

            $Should = @($Asset.name,$Asset._id) -join ","
            $Current = @($CurrentAssetRemote.name,$CurrentAssetRemote._id) -join ","

            if ( $Current -eq $Should ) {
                Write-Host pass -ForegroundColor Green
            } else {
                Write-Host fail -ForegroundColor Red
                $Result.Pass = $false
                $Result."Added Assets" = $false
            }
        }

        $Result
    }

    if ( $PassThru ) {
        $Results
    }
}

function Test-Policy {
    param (
        [Parameter(Mandatory,Position = 0)]
        $JSON,

        [PSTypeName("GCApiKey")]
        $ApiKey,

        [switch]
        $PassThru
    )

    $Test = try {
        Get-GCAsset -Raw -ApiKey $ApiKey
    }
    catch {
        if ( $_.Exception -match "403" ) {
            throw "Authentication error. Please check your API key."
        }
    }

    # Converts to a single string for ConvertFrom-Json
    if ( $JSON.gettype().BaseType -eq "System.Array" ) {
        $JSON = $JSON -join "`n"
    }

    $ShouldPolicy = $JSON | ConvertFrom-Json

    $Results = foreach ( $Policy in $ShouldPolicy ) {
        $CurrentPolicy = Get-GCPolicy -Comments $Policy.comments -Ruleset $Policy.ruleset_name -Section $Policy.section -ApiKey $ApiKey -Limit 1

        if ( $CurrentPolicy ) {
            $CurrentJSON = $CurrentPolicy.published_version | Select-Object -ExcludeProperty *id
            $CurrentJSON | Add-Member -MemberType NoteProperty -Name section -Value $CurrentPolicy.section_position
            $CurrentJSON = $CurrentJSON | ConvertTo-Json -Depth 99    
            $CurrentJSON = Convert-LabelID -JSON $CurrentJSON -ApiKey $ApiKey
            $CurrentPolicy = $CurrentJSON | ConvertFrom-Json
        }

        $Result = [PSCustomObject]@{
            Policy = ($CurrentPolicy.section + " | " + $CurrentPolicy.ruleset_name + " | " + $CurrentPolicy.comments)
            Source = $false
            Destination = $false
            Ports = $false
            Protocols = $false
            Status = $false
            Action = $false
            Pass = $true
        }

        if ( $CurrentPolicy ) {
            Write-Host
            Write-Host $Result.Policy
            Write-Host "source: " -NoNewLine

            $Should = $Policy.source | ConvertTo-Json -Depth 99
            $Current = $CurrentPolicy.source | ConvertTo-Json -Depth 99

            if ( $Current -eq $Should ) {
                $Result.Source = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }

            Write-Host "destination: " -NoNewLine

            $Should = $Policy.destination | ConvertTo-Json -Depth 99
            $Current = $CurrentPolicy.destination | ConvertTo-Json -Depth 99

            if ( $Current -eq $Should ) {
                $Result.Destination = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }

            Write-Host "ports: " -NoNewLine

            $ShouldPorts = $Policy.ports | Sort-Object
            $ShouldExcludePorts = $Policy.exclude_ports | Sort-Object
            $Should = `
            [string]$ShouldPorts,
            [string]$Policy.port_ranges.start,
            [string]$Policy.port_ranges.end,
            [string]$ShouldExcludePorts,
            [string]$Policy.exclude_port_ranges.start,
            [string]$Policy.exclude_port_ranges.end -join ","
        
            $CurrentPorts = $CurrentPolicy.ports | Sort-Object
            $CurrentExcludePorts = $CurrentPolicy.exclude_ports | Sort-Object
            $Current = `
            [string]$CurrentPorts,
            [string]$CurrentPolicy.port_ranges.start,
            [string]$CurrentPolicy.port_ranges.end,
            [string]$CurrentExcludePorts,
            [string]$CurrentPolicy.exclude_port_ranges.start,
            [string]$CurrentPolicy.exclude_port_ranges.end -join ","

            if ( $Current -eq $Should ) {
                $Result.Ports = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }

            Write-Host "protocols: " -NoNewLine

            $CurrentProtocols = $CurrentPolicy.ip_protocols
            [string]$Current = $CurrentProtocols -join ","

            $ShouldProtocols = $Policy.ip_protocols
            [string]$Should = $ShouldProtocols -join ","

            if ( $Current -eq $Should ) {
                $Result.Protocols = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }

            Write-Host "status: " -NoNewLine

            $Should = $Policy.enabled
            $Current = $CurrentPolicy.enabled

            if ( $Current -eq $Should ) {
                $Result.Status = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }

            Write-Host "action: " -NoNewLine

            $Should = $Policy.action
            $Current = $CurrentPolicy.action

            if ( $Current -eq $Should ) {
                $Result.Action = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }
        } else {
            Write-Host
            $MissingPolicy = ($Policy.section + " | " + $Policy.ruleset_name + " | " + $Policy.comments)
            Write-Host $MissingPolicy
            Write-Host "policy doesn't exist" -ForeGroundColor Red
            $Result.Policy = $MissingPolicy
            $Result.Pass = $false
        }

        $Result
    }

    if ( $PassThru ) {
        $Results
    }
}

function Test-Users {
    param (
        [Parameter(Mandatory,Position = 0)]
        $JSON,

        [PSTypeName("GCApiKey")]
        $ApiKey,

        [switch]
        $PassThru
    )

    $Test = try {
        Get-GCAsset -Raw -ApiKey $ApiKey
    }
    catch {
        if ( $_.Exception -match "403" ) {
            throw "Authentication error. Please check your API key."
        }
    }

    # Converts to a single string for ConvertFrom-Json
    if ( $JSON.gettype().BaseType -eq "System.Array" ) {
        $JSON = $JSON -join "`n"
    }

    $ShouldUsers = $JSON | ConvertFrom-Json

    $Results = foreach ( $User in $ShouldUsers ) {
        $CurrentUser = Get-GCUser $User -ApiKey $ApiKey

        $Result = [PSCustomObject]@{
            Username = $User.Username
            Description = $false
            Permissions = $false
            "Two-factor" = $false
            Pass = $true
        }

        if ( $CurrentUser ) {
            Write-Host
            Write-Host $Result.Username
            Write-Host "description: " -NoNewLine

            $Should = $User.Description | ConvertTo-Json -Depth 99
            $Current = $CurrentUser.Description | ConvertTo-Json -Depth 99

            if ( $Current -eq $Should ) {
                $Result.Description = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }

            Write-Host "permissions: " -NoNewLine

            $Should = $Policy.destination | ConvertTo-Json -Depth 99
            $Current = $CurrentPolicy.destination | ConvertTo-Json -Depth 99

            if ( $Current -eq $Should ) {
                $Result.Destination = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }

            Write-Host "ports: " -NoNewLine

            $ShouldPorts = $Policy.ports | Sort-Object
            $ShouldExcludePorts = $Policy.exclude_ports | Sort-Object
            $Should = `
            [string]$ShouldPorts,
            [string]$Policy.port_ranges.start,
            [string]$Policy.port_ranges.end,
            [string]$ShouldExcludePorts,
            [string]$Policy.exclude_port_ranges.start,
            [string]$Policy.exclude_port_ranges.end -join ","
        
            $CurrentPorts = $CurrentPolicy.ports | Sort-Object
            $CurrentExcludePorts = $CurrentPolicy.exclude_ports | Sort-Object
            $Current = `
            [string]$CurrentPorts,
            [string]$CurrentPolicy.port_ranges.start,
            [string]$CurrentPolicy.port_ranges.end,
            [string]$CurrentExcludePorts,
            [string]$CurrentPolicy.exclude_port_ranges.start,
            [string]$CurrentPolicy.exclude_port_ranges.end -join ","

            if ( $Current -eq $Should ) {
                $Result.Ports = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }

            Write-Host "status: " -NoNewLine

            $Should = $Policy.enabled
            $Current = $CurrentPolicy.enabled

            if ( $Current -eq $Should ) {
                $Result.Status = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }

            Write-Host "action: " -NoNewLine

            $Should = $Policy.action
            $Current = $CurrentPolicy.action

            if ( $Current -eq $Should ) {
                $Result.Action = $true
                Write-Host pass -ForeGroundColor Green
            } else {
                $Result.Pass = $false
                Write-host fail -ForeGroundColor Red
            }
        } else {
            Write-Host
            $MissingPolicy = ($Policy.section + " | " + $Policy.ruleset_name + " | " + $Policy.comments)
            Write-Host $MissingPolicy
            Write-Host "policy doesn't exist" -ForeGroundColor Red
            $Result.Policy = $MissingPolicy
            $Result.Pass = $false
        }

        $Result
    }

    if ( $PassThru ) {
        $Results
    }
}

function Update-Labels {
    param (
        [string[]]
        $JSON,

        [PSTypeName("GCApiKey")]
        $ApiKey
    )

    if ( $JSON.GetType().BaseName -eq "System.Array" ) {
        $JSON = $JSON -join "`n"
    }

    $Labels = $JSON | ConvertFrom-Json

    foreach ( $Label in $Labels ) {
        $CurrentLabel = Get-GCLabel -LabelKey $Label.key -LabelValue $Label.value -ApiKey $ApiKey

        if ( -not $CurrentLabel ) {
            New-GCBlankLabel -LabelKey $Label.key -LabelValue $Label.value -ApiKey $ApiKey
            $CurrentLabel = Get-GCLabel -LabelKey $Label.key -LabelValue $Label.value -ApiKey $ApiKey
        }

        $DynamicCriteria = $Label.dynamic_criteria
        $AddedAssets = $Label.added_assets

        # Get assets by id
        $Assets = foreach ($Asset in $AddedAssets) {
            Get-GCAsset -Asset $Asset._id -ApiKey $ApiKey
        }

        if ($DynamicCriteria) {
            $CurrentLabel | Add-Member -MemberType NoteProperty -Name criteria -Value $DynamicCriteria
            $CurrentLabel.dynamic_criteria += $DynamicCriteria
        }

        $CurrentLabel | Set-GCLabel

        if ($AddedAssets) {
            New-GCStaticLabel -LabelKey $Label.key -LabelValue $Label.value -Asset $Assets -ApiKey $ApiKey
        }
    }
}

function Update-Policy {
    [cmdletbinding()]
    param (
        [Parameter(Mandatory,Position = 0)]
        $JSON,

        [Parameter(Mandatory,ParameterSetName = "Publish")]
        $Comments,

        [Parameter(ParameterSetName = "NoPublish")]
        [switch]
        $NoPublish,

        [PSTypeName("GCApiKey")]
        $ApiKey
    )

    $UnpublishedCount = Get-GCPolicy -State "CREATED","MODIFIED","DELETED" -ApiKey $ApiKey -Raw | Select-Object -ExpandProperty total_count
    if ( ($UnpublishedCount -gt 0) -and ($NoPublish -eq $false) ) {
        throw "Unpublished policies on management; aborting"
    }

    # Converting any JSON array input to a string
    if ( $JSON.GetType().BaseName -eq "System.Array" ) {
        $JSON = $JSON -join "`n"
    }

    $ConfigPolicy = $JSON | ConvertFrom-Json

    foreach ( $Policy in $ConfigPolicy ) {
        $CurrentPolicy = Get-GCPolicy -Section $Policy.section -Comments $Policy.comments -Ruleset $Policy.ruleset_name -ApiKey $ApiKey -Limit 1

        if ( $Policy.source.labels ) {
            foreach ( $Group in $Policy.source.labels.or_labels ) {
                $ItemCount = $Group.and_labels.count
                for ( $i = 0; $i -lt $ItemCount; $i++ ) {
                    $Key = $Group.and_labels[$i].Split(":")[0]
                    $Value = $Group.and_labels[$i].Split(":")[1]
                    $Label = Get-GCLabel -LabelKey $Key -LabelValue $Value -ApiKey $ApiKey
                    if ( -not $Label ) {
                        throw "Label mismatch; check labels in management"
                    }
                    $Group.and_labels[$i] = $Label | Select-Object key,value,name,id,color_index
                    $Group.and_labels[$i].name = $Label.key + ": " + $Label.value
                }
            }
        }

        if ( $Policy.destination.labels ) {
            foreach ( $Group in $Policy.destination.labels.or_labels ) {
                $ItemCount = $Group.and_labels.count
                for ( $i = 0; $i -lt $ItemCount; $i++ ) {
                    $Key = $Group.and_labels[$i].Split(":")[0]
                    $Value = $Group.and_labels[$i].Split(":")[1]
                    $Label = Get-GCLabel -LabelKey $Key -LabelValue $Value -ApiKey $ApiKey
                    if ( -not $Label ) {
                        throw "Label mismatch; check labels in management"
                    }
                    $Group.and_labels[$i] = $Label | Select-Object key,value,name,id,color_index
                    $Group.and_labels[$i].name = $Label.key + ": " + $Label.value
                }
            }
        }

        if ( -not $CurrentPolicy ) {
            New-GCPolicy -Section allow -Action allow -Ruleset $Policy.ruleset_name -Comments $Policy.comments -ApiKey $ApiKey
            $CurrentPolicy = Get-GCPolicy -Comments $Policy.comments -Ruleset $Policy.ruleset_name -ApiKey $ApiKey
        }

        $CurrentPolicy.action = $Policy.action
        $CurrentPolicy.section_position = $Policy.section
        $CurrentPolicy.source = $Policy.source
        $CurrentPolicy.destination = $Policy.destination
        if ( $Policy.ports ) {
            $CurrentPolicy.ports = @($Policy.ports)
        }
        if ( $Policy.port_ranges ) {
            $CurrentPolicy.port_ranges = @($Policy.port_ranges)
        }
        if ( $Policy.exclude_ports ) {
            $CurrentPolicy.exclude_ports = @($Policy.exclude_ports)
        }
        if ( $Policy.exclude_port_ranges ) {
            $CurrentPolicy.exclude_port_ranges = @($Policy.exclude_port_ranges)
        }
        $CurrentPolicy.ruleset_name = $Policy.ruleset_name
        $CurrentPolicy.comments = $Policy.comments
        $CurrentPolicy.enabled = $Policy.enabled
        $CurrentPolicy.ip_protocols = $Policy.ip_protocols

        $CurrentPolicy | Set-GCPolicy -ApiKey $ApiKey
    }

    if ( $NoPublish ) {
        return
    }

    Publish-GCPolicy -ApiKey $ApiKey -Comments $Comments
}


Export-ModuleMember -Function ConvertFrom-LabelID,Export-Labels,Export-Policy,Test-Labels,Test-Policy,Test-Users,Update-Labels,Update-Policy