Pinning GitHub Actions to a full-length commit SHA is one of the simplest ways to reduce supply chain risk in your workflows. It ensures that a workflow keeps using the exact code you reviewed, instead of silently following a moving tag or branch.
Tags are convenient, but they are mutable. A workflow that uses actions/checkout@v4 or a third-party action pinned to a tag can start running different code later without any change in your repository. SHA pinning removes that ambiguity. It is a small change, but it gives you a much stronger security posture with very little operational cost.
Note that SHA pinning is not perfect. If the referenced commit contains references to other mutable tags or branches, those references can still change. However, it is a simple and effective way to reduce risk in your workflows. To keep pinned versions current without reverting to mutable tags, use Dependabot or Renovate to help manage updates.
If you manage several personal repositories, enabling that setting one repository at a time is tedious. GitHub already exposes the repository-level Actions permissions API, so you can automate the whole thing with the GitHub CLI.
The script below walks through every repository you own, reads the current Actions permissions, and turns on SHA pinning while preserving the existing settings.
PowerShell
$ErrorActionPreference = "Stop"
$owner = gh api user --jq .login
$repos = gh api --paginate "user/repos?per_page=100&affiliation=owner" `
--jq '.[] | select(.fork == false and .archived == false) | .name'
foreach ($repo in $repos) {
Write-Host "Processing $owner/$repo..."
try {
$settings = gh api "repos/$owner/$repo/actions/permissions" | ConvertFrom-Json
if ($settings.sha_pinning_required) {
Write-Host " Already enabled"
continue
}
$enabled = if ($settings.enabled) { "true" } else { "false" }
gh api `
--method PUT `
"repos/$owner/$repo/actions/permissions" `
-F enabled=$enabled `
-F allowed_actions=$settings.allowed_actions `
-F sha_pinning_required=true `
| Out-Null
Write-Host " ✓ Updated"
}
catch {
Write-Warning " Failed: $($_.Exception.Message)"
}
}
#Additional resources
Do you have a question or a suggestion about this post? Contact me!