modules/Devolutions.CIEM.Graph/Public/Get-CIEMAttackPath.ps1
|
function Get-CIEMAttackPath { [CmdletBinding()] [OutputType('CIEMAttackPath')] param( [Parameter()] [string]$PatternId, [Parameter()] [ValidateSet('critical', 'high', 'medium', 'low')] [string]$Severity, [Parameter()] [string]$PrincipalId ) $ErrorActionPreference = 'Stop' # Load patterns from JSON files $patternDir = Join-Path $script:GraphRoot 'Data' 'attack_paths' $patternFiles = @(Get-ChildItem -Path $patternDir -Filter '*.json' -File -ErrorAction Stop) $patterns = @($patternFiles | ForEach-Object { Get-Content $_.FullName -Raw | ConvertFrom-Json }) # Filter patterns by requested criteria if ($PatternId) { $patterns = @($patterns | Where-Object { $_.id -eq $PatternId }) } if ($Severity) { $patterns = @($patterns | Where-Object { $_.severity -eq $Severity }) } # Evaluate each pattern against the graph $identityKinds = @('EntraUser', 'EntraServicePrincipal', 'EntraGroup', 'EntraManagedIdentity') $findings = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($pattern in $patterns) { $evalParams = @{ Pattern = $pattern } # Constrain seed nodes for identity-first patterns when PrincipalId is specified if ($PrincipalId) { $firstKinds = @($pattern.steps[0].kind) if ($firstKinds | Where-Object { $_ -in $identityKinds }) { $evalParams.SeedNodeId = $PrincipalId } } $results = @(InvokeCIEMAttackPathEvaluation @evalParams) foreach ($r in $results) { $findings.Add($r) } } # Post-filter: only return paths that include the specified principal if ($PrincipalId) { $findings = [System.Collections.Generic.List[PSCustomObject]]@( $findings | Where-Object { if (-not $_.Path) { return $false } $nodeIds = @($_.Path | ForEach-Object { $_.id }) $PrincipalId -in $nodeIds } ) } @($findings) } |