modules/Devolutions.CIEM.Graph/Public/Update-CIEMAttackPath.ps1

function Update-CIEMAttackPath {
    [CmdletBinding()]
    [OutputType('CIEMAttackPath[]')]
    param(
        [Parameter()]
        [string]$PatternId,

        [Parameter()]
        [ValidateSet('critical', 'high', 'medium', 'low')]
        [string]$Severity,

        [Parameter()]
        [switch]$PassThru
    )

    $ErrorActionPreference = 'Stop'

    $patterns = @(GetCIEMAttackPatternDefinition)
    if ($PatternId) {
        $patterns = @($patterns | Where-Object { $_.id -eq $PatternId })
    }
    if ($Severity) {
        $patterns = @($patterns | Where-Object { $_.severity -eq $Severity })
    }

    $refreshRuleIds = @($patterns | ForEach-Object { [string]$_.id })
    if ($refreshRuleIds.Count -gt 0) {
        $deleteParams = @{}
        $deletePlaceholders = @()
        for ($i = 0; $i -lt $refreshRuleIds.Count; $i++) {
            $key = "rule$i"
            $deleteParams[$key] = $refreshRuleIds[$i]
            $deletePlaceholders += "@$key"
        }

        Invoke-CIEMQuery -Query "DELETE FROM attack_paths WHERE rule_id IN ($($deletePlaceholders -join ', '))" -Parameters $deleteParams -AsNonQuery | Out-Null
    }

    $evaluatedAt = (Get-Date).ToString('o')
    $findings = [System.Collections.Generic.List[CIEMAttackPath]]::new()

    foreach ($pattern in $patterns) {
        foreach ($attackPath in @(InvokeCIEMAttackPathEvaluation -Pattern $pattern)) {
            $attackPath.EvaluatedAt = $evaluatedAt
            $pathJson = @($attackPath.Path) | ConvertTo-Json -Depth 20 -Compress
            $edgesJson = @($attackPath.Edges) | ConvertTo-Json -Depth 20 -Compress

            if ([string]::IsNullOrWhiteSpace($pathJson)) {
                throw "Cannot store attack path '$($attackPath.Id)' because path JSON is empty."
            }

            if ([string]::IsNullOrWhiteSpace($edgesJson)) {
                throw "Cannot store attack path '$($attackPath.Id)' because edges JSON is empty."
            }

            Invoke-CIEMQuery -Query @"
INSERT OR REPLACE INTO attack_paths (
    id, rule_id, pattern_name, severity, category, remediation, psu_script_name,
    path_json, edges_json, path_chain, evaluated_at
) VALUES (
    @id, @rule_id, @pattern_name, @severity, @category, @remediation, @psu_script_name,
    @path_json, @edges_json, @path_chain, @evaluated_at
)
"@
 -Parameters @{
                id              = $attackPath.Id
                rule_id         = $attackPath.PatternId
                pattern_name    = $attackPath.PatternName
                severity        = $attackPath.Severity
                category        = $attackPath.Category
                remediation     = $attackPath.Remediation
                psu_script_name = $attackPath.PsuScriptName
                path_json       = $pathJson
                edges_json      = $edgesJson
                path_chain      = $attackPath.PathChain
                evaluated_at    = $attackPath.EvaluatedAt
            } -AsNonQuery | Out-Null

            $findings.Add($attackPath)
        }
    }

    if ($PassThru) {
        @($findings)
    }
}