Public/Test-ForkPullPolicy.ps1

function Test-ForkPullPolicy {
    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param(
        [Parameter(Mandatory)]
        [PSCustomObject[]]$WorkflowFiles
    )

    $results = [System.Collections.Generic.List[PSCustomObject]]::new()

    $prtPatterns = @(
        '(?m)^\s*pull_request_target\s*:'
        '(?m)^\s*on\s*:\s*pull_request_target\s*(?:#.*)?$'
        '(?m)^\s*on\s*:\s*\[[^\]]*\bpull_request_target\b[^\]]*\]'
    )

    # Checkout of untrusted PR head (any of these = fork-supplied ref)
    $forkRefPatterns = @(
        '\$\{\{\s*github\.event\.pull_request\.head\.sha\s*\}\}'
        '\$\{\{\s*github\.event\.pull_request\.head\.ref\s*\}\}'
        '\$\{\{\s*github\.head_ref\s*\}\}'
    )

    foreach ($wf in $WorkflowFiles) {
        $stripped = (($wf.Content -split "`n") | Where-Object { $_ -notmatch '^\s*#' }) -join "`n"

        $hasPRT = $false
        foreach ($pattern in $prtPatterns) {
            if ($stripped -match $pattern) { $hasPRT = $true; break }
        }

        if (-not $hasPRT) {
            $results.Add((Format-FylgyrResult `
                -CheckName 'ForkPullPolicy' `
                -Status 'Pass' `
                -Severity 'Info' `
                -Resource $wf.Path `
                -Detail "Workflow '$($wf.Name)' does not use pull_request_target." `
                -Remediation 'No action needed.'))
            continue
        }

        $forkRefMatch = $null
        foreach ($pattern in $forkRefPatterns) {
            if ($stripped -match $pattern) {
                $forkRefMatch = $Matches[0]
                break
            }
        }

        if (-not $forkRefMatch) {
            $results.Add((Format-FylgyrResult `
                -CheckName 'ForkPullPolicy' `
                -Status 'Pass' `
                -Severity 'Info' `
                -Resource $wf.Path `
                -Detail "Workflow '$($wf.Name)' uses pull_request_target but does not check out the PR head ref. The fork trust boundary is preserved." `
                -Remediation 'No action needed. Continue to avoid checking out untrusted refs in pull_request_target workflows.'))
            continue
        }

        $results.Add((Format-FylgyrResult `
            -CheckName 'ForkPullPolicy' `
            -Status 'Fail' `
            -Severity 'High' `
            -Resource $wf.Path `
            -Detail "Workflow '$($wf.Name)' uses pull_request_target and checks out a fork-controlled ref ($forkRefMatch). This grants fork PRs access to repository secrets and a write-capable GITHUB_TOKEN, which is the exact primitive abused by the nx Pwn Request and tj-actions/changed-files (Shai-Hulud) incidents." `
            -Remediation 'Never combine pull_request_target with a checkout of github.event.pull_request.head.* or github.head_ref. Use the pull_request trigger for untrusted code, and split secret-dependent steps into a separate workflow_run workflow with environment protection.' `
            -AttackMapping @('nx-pwn-request', 'tj-actions-shai-hulud', 'prt-scan-ai-automated')))
    }

    $results.ToArray()
}