Public/Test-DangerousTrigger.ps1
|
function Test-DangerousTrigger { [CmdletBinding()] [OutputType([PSCustomObject[]])] param( [Parameter(Mandatory)] [PSCustomObject[]]$WorkflowFiles ) $results = [System.Collections.Generic.List[PSCustomObject]]::new() $dangerousTriggers = @('pull_request_target', 'workflow_run') # Patterns that indicate checkout of untrusted PR code $untrustedCheckoutPatterns = @( 'github\.event\.pull_request\.head\.sha' 'github\.event\.pull_request\.head\.ref' '\$\{\{\s*github\.head_ref\s*\}\}' ) foreach ($wf in $WorkflowFiles) { # Strip YAML comment lines to avoid false positives $content = (($wf.Content -split "`n") | Where-Object { $_ -notmatch '^\s*#' }) -join "`n" $foundTriggers = @() foreach ($trigger in $dangerousTriggers) { $escaped = [regex]::Escape($trigger) $triggerPatterns = @( "(?m)^\s*$escaped\s*:" "(?m)^\s*on\s*:\s*$escaped\s*(?:#.*)?$" "(?m)^\s*on\s*:\s*\[[^\]]*\b$escaped\b[^\]]*\]" ) foreach ($tp in $triggerPatterns) { if ($content -match $tp) { $foundTriggers += $trigger break } } } if ($foundTriggers.Count -eq 0) { $results.Add((Format-FylgyrResult ` -CheckName 'DangerousTrigger' ` -Status 'Pass' ` -Severity 'Info' ` -Resource $wf.Path ` -Detail 'No dangerous trigger patterns found.' ` -Remediation 'None.')) continue } # Check if the workflow checks out untrusted code $checksOutUntrusted = $false foreach ($pattern in $untrustedCheckoutPatterns) { if ($content -match $pattern) { $checksOutUntrusted = $true break } } $triggerList = $foundTriggers -join ', ' if ($checksOutUntrusted) { $results.Add((Format-FylgyrResult ` -CheckName 'DangerousTrigger' ` -Status 'Fail' ` -Severity 'Critical' ` -Resource $wf.Path ` -Detail "Uses $triggerList and checks out untrusted PR code. This may allow attacker-controlled code to run with elevated permissions unless explicitly restricted." ` -Remediation 'Do not checkout the PR head ref in pull_request_target workflows. Use pull_request trigger instead, or run untrusted code in a separate unprivileged workflow.' ` -AttackMapping @('nx-pwn-request'))) } else { $results.Add((Format-FylgyrResult ` -CheckName 'DangerousTrigger' ` -Status 'Warning' ` -Severity 'Medium' ` -Resource $wf.Path ` -Detail "Uses $triggerList without apparent checkout of untrusted code. The workflow may still run with elevated permissions." ` -Remediation 'Verify this workflow does not process untrusted input. Consider narrowing permissions or switching to pull_request trigger.' ` -AttackMapping @('nx-pwn-request'))) } } return $results.ToArray() } |