Public/Test-ArtifactAttestation.ps1
|
function Test-ArtifactAttestation { [CmdletBinding()] [OutputType([PSCustomObject[]])] param( [Parameter(Mandatory)] [PSCustomObject[]]$WorkflowFiles ) $results = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($wf in $WorkflowFiles) { $content = (($wf.Content -split "`n") | Where-Object { $_ -notmatch '^\s*#' }) -join "`n" $jobBlocks = @(Get-WorkflowJobBlock -Content $content) $releaseJobsMissingAttestation = [System.Collections.Generic.List[string]]::new() $foundReleaseJob = $false foreach ($job in $jobBlocks) { $jobText = $job.Content $hasDockerPush = $jobText -match '(?im)^\s*-\s*uses\s*:\s*docker/build-push-action@' -and $jobText -match '(?im)^\s*push\s*:\s*true\s*$' $isReleaseJob = ($jobText -match '(?i)\bnpm\s+publish\b') -or ($jobText -match '(?i)\bpypa/gh-action-pypi-publish@') -or ($jobText -match '(?i)\bsoftprops/action-gh-release@') -or ($jobText -match '(?i)\bactions/upload-release-asset@') -or ($jobText -match '(?i)\bgh\s+release\s+create\b') -or $hasDockerPush if (-not $isReleaseJob) { continue } $foundReleaseJob = $true $hasAttestationStep = ($jobText -match '(?im)^\s*-\s*uses\s*:\s*actions/attest-build-provenance@') -or ($jobText -match '(?im)^\s*-\s*uses\s*:\s*actions/attest@') $hasIdTokenWrite = $jobText -match '(?im)^\s*id-token\s*:\s*write\s*$' $hasAttestationsWrite = $jobText -match '(?im)^\s*attestations\s*:\s*write\s*$' if (-not $hasAttestationStep -or -not $hasIdTokenWrite -or -not $hasAttestationsWrite) { $releaseJobsMissingAttestation.Add($job.Name) } } if (-not $foundReleaseJob) { $results.Add((Format-FylgyrResult ` -CheckName 'ArtifactAttestation' ` -Status 'Pass' ` -Severity 'Info' ` -Resource $wf.Path ` -Detail "Workflow '$($wf.Name)' does not appear to produce release artifacts." ` -Remediation 'No action needed.')) continue } if ($releaseJobsMissingAttestation.Count -gt 0) { $missingJobs = @($releaseJobsMissingAttestation | Sort-Object -Unique) -join ', ' $results.Add((Format-FylgyrResult ` -CheckName 'ArtifactAttestation' ` -Status 'Warning' ` -Severity 'Medium' ` -Resource $wf.Path ` -Detail "Release-producing job(s) in workflow '$($wf.Name)' are missing full provenance attestation controls (actions/attest-build-provenance or actions/attest with id-token: write + attestations: write): $missingJobs. Cross-check with PublishIntegrity; passing publish controls without provenance attestation still leaves integrity blind spots." ` -Remediation 'For each release-producing job, add actions/attest-build-provenance (or actions/attest) and grant id-token: write plus attestations: write in permissions.' ` -AttackMapping @('solarwinds-orion', 'codecov-bash-uploader'))) continue } $results.Add((Format-FylgyrResult ` -CheckName 'ArtifactAttestation' ` -Status 'Pass' ` -Severity 'Info' ` -Resource $wf.Path ` -Detail "All release-producing jobs in workflow '$($wf.Name)' include provenance attestation signals." ` -Remediation 'No action needed.')) } return $results.ToArray() } |