modules/Devolutions.CIEM.PSU/Pages/New-CIEMAttackPathsPage.ps1

function New-CIEMAttackPathsPage {
    <#
    .SYNOPSIS
        Creates the Attack Paths page with a data grid of discovered attack path findings.
    .PARAMETER Navigation
        Array of UDListItem components for sidebar navigation.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object[]]$Navigation
    )

    $ErrorActionPreference = 'Stop'

    New-UDPage -Name 'Attack Paths' -Url '/ciem/attack-paths' -Content {
        New-UDTypography -Text 'Attack Paths' -Variant 'h4' -Style @{ marginBottom = '10px'; marginTop = '10px' }
        New-UDTypography -Text 'Discovered attack paths evaluated against the security graph' -Variant 'subtitle1' -Style @{ marginBottom = '20px'; opacity = 0.7 }

        New-UDButton -Id 'refreshAttackPathsBtn' -Text 'Refresh Attack Paths' -Variant 'outlined' -Color 'secondary' -ShowLoading -OnClick {
            $attackPathCount = @(Devolutions.CIEM\Update-CIEMAttackPath -PassThru).Count
            Sync-UDElement -Id 'attackPathsPanel'
            Show-UDToast -Message "Attack paths refreshed: $attackPathCount findings" -Duration 5000 -BackgroundColor '#4caf50'
        } -Style @{ marginBottom = '16px' }

        New-UDCard -Content {
            New-UDDynamic -Id 'attackPathsPanel' -Content {

                try {
                    $hasData = (Devolutions.CIEM\Invoke-CIEMQuery -Query 'SELECT COUNT(*) as c FROM graph_nodes').c -gt 0

                    if ($hasData) {
                        New-UDDataGrid -LoadRows {

                            $findings = @(Devolutions.CIEM\Get-CIEMAttackPath)
                            $idx = 0
                            $gridData = $findings | ForEach-Object {
                                $idx++
                                $chainLabels = @($_.Path | ForEach-Object {
                                    $label = if ($_.display_name) { $_.display_name } else { $_.kind }
                                    "$label ($($_.kind))"
                                })
                                $chainText = $chainLabels -join ' → '

                                @{
                                    id            = "$($_.PatternId)-$idx"
                                    attackPathId  = $_.Id
                                    patternName   = $_.PatternName
                                    severity      = $_.Severity
                                    category      = $_.Category
                                    pathChain     = $chainText
                                    steps         = @($_.Path).Count
                                    remediation   = $_.Remediation
                                    psuScriptName = $_.PsuScriptName
                                }
                            }
                            @($gridData) | Out-UDDataGridData -Context $EventData -TotalRows @($gridData).Count
                        } -Columns @(
                            New-UDDataGridColumn -Field 'patternName' -HeaderName 'Pattern Name' -Flex 1
                            New-UDDataGridColumn -Field 'severity' -HeaderName 'Severity' -Width 130 -Render {
                                $color = Devolutions.CIEM\Get-SeverityColor -Severity $EventData.severity
                                New-UDChip -Label $EventData.severity -Size 'small' -Style @{ backgroundColor = $color; color = 'white' }
                            }
                            New-UDDataGridColumn -Field 'category' -HeaderName 'Category' -Width 200
                            New-UDDataGridColumn -Field 'pathChain' -HeaderName 'Path Chain' -Flex 2 -Render {
                                New-UDTypography -Text $EventData.pathChain -Variant 'body2' -Style @{ fontFamily = 'monospace'; opacity = 0.8 }
                            }
                            New-UDDataGridColumn -Field 'steps' -HeaderName 'Steps' -Width 90 -Type 'number'
                        ) -AutoHeight $true -Pagination -PageSize 25 -ShowQuickFilter -LoadDetailContent {
                            $remediation = [string]$EventData.row.remediation
                            $attackPathId = [string]$EventData.row.attackPathId
                            $remediationScript = Devolutions.CIEM\Get-CIEMAttackPathRemediationScript -Id $attackPathId
                            $executionKey = [guid]::NewGuid().ToString('N')
                            $executionStreamsId = "ciemAttackPathExecutionStreams_$executionKey"
                            $executionWarningId = "ciemAttackPathExecutionWarning_$executionKey"
                            $scriptActionButtonStyle = @{
                                width           = '112px'
                                minWidth        = '112px'
                                height          = '32px'
                                display         = 'inline-flex'
                                alignItems      = 'center'
                                justifyContent  = 'center'
                                padding         = '0'
                                backgroundColor = '#ffffff'
                                border          = '1px solid #d0d7de'
                                borderRadius    = '6px'
                                color           = '#57606a'
                                cursor          = 'pointer'
                                textDecoration  = 'none'
                                fontSize        = '12px'
                                fontWeight      = '600'
                                fontFamily      = 'inherit'
                                lineHeight      = '1'
                                textTransform   = 'none'
                            }
                            New-UDElement -Tag 'div' -Attributes @{ style = @{ padding = '14px 18px'; display = 'grid'; gap = '14px' } } -Content {
                                New-UDElement -Tag 'section' -Content {
                                    New-UDTypography -Text 'Remediation' -Variant 'subtitle2' -Style @{ fontWeight = '600'; marginBottom = '6px' }
                                    New-UDElement -Tag 'pre' -Attributes @{
                                        'data-ciem-attack-path-remediation' = 'true'
                                        style = @{
                                            margin = '0'
                                            padding = '12px'
                                            border = '1px solid #d0d7de'
                                            borderRadius = '6px'
                                            backgroundColor = '#ffffff'
                                            whiteSpace = 'pre-wrap'
                                            overflowWrap = 'anywhere'
                                            fontFamily = 'inherit'
                                            fontSize = '14px'
                                            lineHeight = '1.45'
                                            maxHeight = '220px'
                                            overflow = 'auto'
                                        }
                                    } -Content {
                                        $remediation
                                    }
                                }
                                New-UDElement -Tag 'section' -Content {
                                    New-UDTypography -Text 'Remediation Script' -Variant 'subtitle2' -Style @{ fontWeight = '600'; marginBottom = '6px' }
                                    New-UDElement -Tag 'div' -Attributes @{ style = @{ position = 'relative' } } -Content {
                                        New-UDElement -Tag 'div' -Attributes @{
                                            'data-ciem-attack-path-remediation-script-actions' = 'true'
                                            style = @{
                                                position   = 'absolute'
                                                top        = '8px'
                                                right      = '8px'
                                                zIndex     = 2
                                                display    = 'flex'
                                                alignItems = 'center'
                                                gap        = '8px'
                                            }
                                        } -Content {
                                            New-UDElement -Tag 'div' -Attributes @{
                                                'data-ciem-attack-path-remediation-script-copy' = 'true'
                                            } -Content {
                                                New-UDElement -Tag 'a' -Attributes @{
                                                    href = @'
javascript:(()=>{const control=document.activeElement;const panel=control.closest(".MuiDataGrid-detailPanel");const scriptBlock=panel.querySelector("[data-ciem-attack-path-remediation-script='true']");const textArea=document.createElement("textarea");textArea.value=scriptBlock.textContent.trim();textArea.setAttribute("readonly","");textArea.style.position="fixed";textArea.style.top="-1000px";textArea.style.left="-1000px";document.body.appendChild(textArea);textArea.focus();textArea.select();document.execCommand("copy");document.body.removeChild(textArea);const idle=control.querySelector("[data-ciem-copy-idle='true']");const success=control.querySelector("[data-ciem-copy-success='true']");idle.style.display="none";success.style.display="inline-flex";control.style.borderColor="#2da44e";control.style.color="#1a7f37";control.title="Copied";control.setAttribute("aria-label","Copied remediation script");window.clearTimeout(control.__ciemCopyTimer);control.__ciemCopyTimer=window.setTimeout(()=>{idle.style.display="inline-flex";success.style.display="none";control.style.borderColor="#d0d7de";control.style.color="#57606a";control.title="Copy script";control.setAttribute("aria-label","Copy remediation script");},1800);})()
'@

                                                    role = 'button'
                                                    title = 'Copy script'
                                                    'aria-label' = 'Copy remediation script'
                                                    style = $scriptActionButtonStyle
                                                } -Content {
                                                    New-UDElement -Tag 'span' -Attributes @{
                                                        'data-ciem-copy-idle' = 'true'
                                                        style = @{ display = 'inline-flex'; alignItems = 'center'; gap = '6px' }
                                                    } -Content {
                                                        New-UDIcon -Icon 'Copy' -Size 'sm'
                                                        New-UDElement -Tag 'span' -Content { 'Copy' }
                                                    }
                                                    New-UDElement -Tag 'span' -Attributes @{
                                                        'data-ciem-copy-success' = 'true'
                                                        style = @{ display = 'none'; alignItems = 'center'; gap = '6px' }
                                                    } -Content {
                                                        New-UDIcon -Icon 'CheckCircle' -Size 'sm'
                                                        New-UDElement -Tag 'span' -Content { 'Copied' }
                                                    }
                                                }
                                            }
                                            New-UDElement -Tag 'div' -Attributes @{
                                                'data-ciem-attack-path-remediation-script-execute' = 'true'
                                            } -Content {
                                                New-UDButton -Text 'Execute' -Variant 'outlined' -Color 'secondary' -Size 'small' -Icon (New-UDIcon -Icon 'Play' -Size 'sm') -Style $scriptActionButtonStyle -OnClick {
                                                $job = Invoke-PSUScript -Name 'Checks/Invoke-CIEMAttackPathRemediation' -Integrated -Parameters @{ AttackPathId = $attackPathId }
                                                $Session:CIEMAttackPathExecution = @{
                                                    JobId        = [int64]$job.Id
                                                    Script       = $remediationScript
                                                    Status       = [string]$job.Status
                                                    IsRunning    = $true
                                                    CloseWarning = $false
                                                }

                                                Show-UDModal -Persistent -FullWidth -MaxWidth 'lg' -Header {
                                                    New-UDTypography -Text 'Execute Remediation Script' -Variant 'h6'
                                                } -Content {
                                                    New-UDElement -Tag 'div' -Attributes @{
                                                        'data-ciem-attack-path-execution-dialog' = 'true'
                                                        style = @{ display = 'grid'; gap = '14px' }
                                                    } -Content {
                                                        New-UDTypography -Text 'Script' -Variant 'subtitle2' -Style @{ fontWeight = '600' }
                                                        New-UDElement -Tag 'pre' -Attributes @{
                                                            'data-ciem-attack-path-execution-script' = 'true'
                                                            style = @{
                                                                margin = '0'
                                                                padding = '12px'
                                                                border = '1px solid #d0d7de'
                                                                borderRadius = '6px'
                                                                backgroundColor = '#ffffff'
                                                                whiteSpace = 'pre-wrap'
                                                                overflowWrap = 'anywhere'
                                                                fontFamily = 'monospace'
                                                                fontSize = '13px'
                                                                lineHeight = '1.45'
                                                                maxHeight = '220px'
                                                                overflow = 'auto'
                                                            }
                                                        } -Content {
                                                            $Session:CIEMAttackPathExecution['Script']
                                                        }
                                                        New-UDTypography -Text 'Streams' -Variant 'subtitle2' -Style @{ fontWeight = '600' }
                                                        New-UDDynamic -Id $executionStreamsId -AutoRefresh -AutoRefreshInterval 1 -Content {
                                                            $executionState = $Session:CIEMAttackPathExecution
                                                            if ($null -eq $executionState) {
                                                                throw 'Cannot render attack path remediation execution because execution state is missing.'
                                                            }

                                                            $jobId = [int64]$executionState['JobId']
                                                            $job = Get-PSUJob -Id $jobId -Integrated
                                                            $streamLines = [System.Collections.Generic.List[string]]::new()
                                                            $streamLines.Add("Execution started. Job $jobId.")

                                                            foreach ($message in @(Devolutions.CIEM\Get-CIEMPSUJobOutput -Job $job -Integrated)) {
                                                                $streamLines.Add("[stream] $message")
                                                            }

                                                            $isRunning = @('Queued', 'Running') -contains [string]$job.Status
                                                            if (-not $isRunning) {
                                                                $streamLines.Add("Execution $($job.Status).")
                                                            }

                                                            $streamText = $streamLines -join [Environment]::NewLine
                                                            $executionState['Status'] = [string]$job.Status
                                                            $executionState['IsRunning'] = $isRunning
                                                            $executionState['Streams'] = $streamText
                                                            $Session:CIEMAttackPathExecution = $executionState

                                                            New-UDElement -Tag 'div' -Attributes @{
                                                                'data-ciem-attack-path-execution-streams' = 'true'
                                                            } -Content {
                                                                New-UDTextbox -Multiline -Rows 14 -FullWidth -Disabled -Value $streamText
                                                            }
                                                        }
                                                        New-UDDynamic -Id $executionWarningId -Content {
                                                            $executionState = $Session:CIEMAttackPathExecution
                                                            if ($null -eq $executionState) {
                                                                throw 'Cannot render attack path remediation close warning because execution state is missing.'
                                                            }

                                                            if ([bool]$executionState['CloseWarning']) {
                                                                New-UDElement -Tag 'div' -Attributes @{
                                                                    'data-ciem-attack-path-execution-close-warning' = 'true'
                                                                    style = @{ display = 'grid'; gap = '10px' }
                                                                } -Content {
                                                                    New-UDAlert -Severity 'warning' -Title 'Script still running' -Text 'Terminate it now or leave it running in the background.'
                                                                    New-UDElement -Tag 'div' -Attributes @{ style = @{ display = 'flex'; gap = '8px' } } -Content {
                                                                        New-UDElement -Tag 'div' -Attributes @{
                                                                            'data-ciem-attack-path-execution-terminate' = 'true'
                                                                            'data-ciem-attack-path-execution-warning-terminate' = 'true'
                                                                        } -Content {
                                                                            New-UDButton -Text 'Terminate' -Variant 'contained' -Color 'error' -OnClick {
                                                                                $executionState = $Session:CIEMAttackPathExecution
                                                                                if ($null -eq $executionState) {
                                                                                    throw 'Cannot terminate attack path remediation because execution state is missing.'
                                                                                }

                                                                                $jobId = [int64]$executionState['JobId']
                                                                                $job = Get-PSUJob -Id $jobId -Integrated
                                                                                if (@('Queued', 'Running') -contains [string]$job.Status) {
                                                                                    Stop-PSUJob -Id $jobId -Integrated | Out-Null
                                                                                }
                                                                                Hide-UDModal
                                                                            }
                                                                        }
                                                                        New-UDElement -Tag 'div' -Attributes @{
                                                                            'data-ciem-attack-path-execution-leave-running' = 'true'
                                                                        } -Content {
                                                                            New-UDButton -Text 'Leave Running' -Variant 'outlined' -Color 'secondary' -OnClick {
                                                                                $executionState = $Session:CIEMAttackPathExecution
                                                                                if ($null -eq $executionState) {
                                                                                    throw 'Cannot leave attack path remediation running because execution state is missing.'
                                                                                }

                                                                                $executionState['CloseWarning'] = $false
                                                                                $Session:CIEMAttackPathExecution = $executionState
                                                                                Hide-UDModal
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                } -Footer {
                                                    New-UDElement -Tag 'div' -Attributes @{
                                                        style = @{ display = 'flex'; gap = '8px'; justifyContent = 'flex-end'; width = '100%' }
                                                    } -Content {
                                                        New-UDElement -Tag 'div' -Attributes @{
                                                            'data-ciem-attack-path-execution-terminate' = 'true'
                                                        } -Content {
                                                            New-UDButton -Text 'Terminate' -Variant 'outlined' -Color 'error' -OnClick {
                                                                $executionState = $Session:CIEMAttackPathExecution
                                                                if ($null -eq $executionState) {
                                                                    throw 'Cannot terminate attack path remediation because execution state is missing.'
                                                                }

                                                                $jobId = [int64]$executionState['JobId']
                                                                $job = Get-PSUJob -Id $jobId -Integrated
                                                                if (@('Queued', 'Running') -contains [string]$job.Status) {
                                                                    Stop-PSUJob -Id $jobId -Integrated | Out-Null
                                                                }
                                                                Hide-UDModal
                                                            }
                                                        }
                                                        New-UDElement -Tag 'div' -Attributes @{
                                                            'data-ciem-attack-path-execution-close' = 'true'
                                                        } -Content {
                                                            New-UDButton -Text 'Close' -Variant 'contained' -Color 'primary' -OnClick {
                                                                $executionState = $Session:CIEMAttackPathExecution
                                                                if ($null -eq $executionState) {
                                                                    throw 'Cannot close attack path remediation execution because execution state is missing.'
                                                                }

                                                                $jobId = [int64]$executionState['JobId']
                                                                $job = Get-PSUJob -Id $jobId -Integrated
                                                                $isRunning = @('Queued', 'Running') -contains [string]$job.Status
                                                                $executionState['Status'] = [string]$job.Status
                                                                $executionState['IsRunning'] = $isRunning

                                                                if ($isRunning) {
                                                                    $executionState['CloseWarning'] = $true
                                                                    $Session:CIEMAttackPathExecution = $executionState
                                                                    Sync-UDElement -Id $executionWarningId
                                                                }
                                                                else {
                                                                    Hide-UDModal
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                        }
                                        New-UDElement -Tag 'pre' -Attributes @{
                                            'data-ciem-attack-path-remediation-script' = 'true'
                                            style = @{
                                                margin = '0'
                                                padding = '12px 260px 12px 12px'
                                                border = '1px solid #d0d7de'
                                                borderRadius = '6px'
                                                backgroundColor = '#ffffff'
                                                whiteSpace = 'pre-wrap'
                                                overflowWrap = 'anywhere'
                                                fontFamily = 'monospace'
                                                fontSize = '13px'
                                                lineHeight = '1.45'
                                                maxHeight = '320px'
                                                overflow = 'auto'
                                            }
                                        } -Content {
                                            $remediationScript
                                        }
                                    }
                                }
                                New-UDTypography -Text 'Path Chain' -Variant 'h6' -Style @{ marginTop = '12px'; marginBottom = '4px' }
                                New-UDTypography -Text $EventData.row.pathChain -Variant 'body2' -Style @{ fontFamily = 'monospace'; opacity = 0.8 }
                            }
                        }
                    }
                    else {
                        New-UDTypography -Text 'No attack path data available. Run Azure Discovery from the Environment page to build the security graph.' -Variant 'body2' -Style @{ opacity = 0.5; fontStyle = 'italic'; padding = '16px' }
                    }
                }
                catch {
                    New-UDTypography -Text 'Unable to load attack path data.' -Variant 'body2' -Style @{ opacity = 0.5; fontStyle = 'italic'; padding = '16px' }
                }
            }
        }
    } -Navigation $Navigation -NavigationLayout permanent
}