plugins/secure/skills/action-pinning-overview/SKILL.md
Why pinning GitHub Actions to SHA-256 commits matters for supply chain security. Attack vectors from unpinned actions and comparison of tag vs SHA pinning.
npx skillsauth add adaptive-enforcement-lab/claude-skills action-pinning-overviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Lock down your GitHub Actions supply chain. Unpinned actions are the fastest route to a compromised CI/CD pipeline.
The Risk
Every action in your workflow executes with access to your repository secrets, cloud credentials, and deployment permissions. A single compromised action can exfiltrate everything.
GitHub Actions workflows pull third-party code directly into your CI/CD pipeline. Without pinning, you're trusting that:
Reality: All four scenarios have occurred in production environments.
flowchart TD
A["Unpinned Action<br/>uses: actions/checkout@v4"] --> B["Tag Reference"]
B --> C{"Maintainer Updates Tag"}
C -->|Legitimate Update| D["New Features"]
C -->|Compromised Account| E["Malicious Code"]
C -->|Hijacked Repo| F["Backdoor Injection"]
E --> G["Exfiltrate Secrets"]
E --> H["Deploy Backdoor"]
E --> I["Modify Code"]
F --> G
F --> H
F --> I
%% Ghostty Hardcore Theme
style A fill:#f92572,color:#1b1d1e
style B fill:#fd971e,color:#1b1d1e
style C fill:#e6db74,color:#1b1d1e
style D fill:#a6e22e,color:#1b1d1e
style E fill:#f92572,color:#1b1d1e
style F fill:#f92572,color:#1b1d1e
style G fill:#66d9ef,color:#1b1d1e
style H fill:#66d9ef,color:#1b1d1e
style I fill:#66d9ef,color:#1b1d1e
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
The Problem: Tags are mutable. Maintainers can update v4 to point to any commit. You have no guarantee the code hasn't changed since you tested it.
Attack Vector: Compromised maintainer moves tag to malicious commit. Every workflow using that tag now executes attacker code.
# actions/checkout v4.1.1
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
# actions/setup-node v3.8.1
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d
The Defense: SHA-256 commit hashes are immutable. The code at that hash cannot change. You're pinning to exact, verified code.
Comment Strategy: Include the semantic version in a comment so humans know what SHA represents.
| Aspect | Tag Reference | SHA Pinning |
| ------ | ------------- | ----------- |
| Mutability | Tag can move to any commit | SHA is immutable |
| Supply Chain Risk | High - trust maintainer forever | Low - trust specific commit |
| Update Visibility | Silent updates | Explicit updates via PR |
| Compromise Detection | Difficult - looks like normal update | Clear - SHA change triggers review |
| Dependabot Support | Yes | Yes |
| Human Readability | Good (v4) | Poor (b4ffde6...) without comments |
| Security Posture | Vulnerable | Hardened |
Timeline:
v3 tag to point to backdoored commitImpact: Multi-organization breach. Secrets, credentials, and source code compromised across hundreds of repositories.
Prevention: SHA pinning. Workflows continue using verified commit. Dependabot flags the tag update for review.
Timeline:
Impact: Supply chain compromise affecting downstream users. Deployment credentials stolen, production systems compromised.
Prevention: SHA pinning with Dependabot review. Team reviews changelog and diff before approving SHA update.
Timeline:
actions/check0ut (zero instead of 'o')Impact: Developer typo leads to cloud account compromise. Thousands in cloud costs, potential data breach.
Prevention: SHA pinning forces explicit review. Full action path visible in security review. Allowlisting blocks unknown actions.
actions/checkout, actions/setup-node, actions/upload-artifactaws-actions/*, azure/*, google-github-actions/*Mechanism: Attacker with write access moves tag to malicious commit.
# Attacker commands
git tag -d v3
git tag v3 <malicious-commit-sha>
git push --force --tags
Result: All workflows using @v3 now execute malicious code. No PR, no review, no notification.
Mechanism: Action imports malicious package via package manager inside action code.
Example:
# Action looks safe
- uses: trusted-org/deploy-action@v2
But inside trusted-org/deploy-action:
// action.js imports compromised package
const utils = require('internal-deploy-utils'); // Typosquatted package
Result: Even SHA-pinned action can be compromised if it pulls unpinned dependencies.
Defense: Review action source code, check action's own dependencies, use Dependabot for action repos.
Mechanism: Attacker uses stolen credentials or session hijacking to access maintainer account.
Attack Path:
Result: Widespread compromise across all users of the action.
Defense: SHA pinning breaks attack chain. Workflows don't auto-update to compromised version.
name: Secure CI
on: [push]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
# SHA-pinned actions with version comments
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
Dependabot monitors your workflow files and creates PRs for action updates:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "github-actions"
Process:
Internal Actions (same organization):
# Internal shared action
- uses: my-org/shared-workflows/.github/actions/deploy@main
Consideration: If you control the repository and trust your team, tag references acceptable. Still recommend SHA pinning for audit trail.
Docker Container Actions:
# Action runs in container
- uses: docker://alpine:3.18
Consideration: Container images have their own pinning strategy (digest-based). Apply same principles:
# Digest-pinned container
- uses: docker://alpine@sha256:abc123...
Organization-level policy restricts which actions can be used:
Settings → Actions → General → Allow select actions and reusable workflows
Add verified actions to allowlist. Blocks unknown actions at workflow runtime.
Require status checks for workflows that modify action pins:
.github/workflows/* changesTrack action usage across organization:
workflow_jobReady to implement SHA pinning? Continue with:
| Risk | Mitigation | Effort | | ---- | ---------- | ------ | | Tag mutation | SHA pinning | Low | | Compromised maintainer | SHA pinning + review | Medium | | Typosquatting | Allowlisting | Medium | | Dependency confusion | Source review | High | | Silent updates | Dependabot + PR review | Low |
Start Today
Pin your most critical workflows first. Focus on workflows with:
- Production deployment access
- Cloud credential usage
- Cross-repository permissions
Use automation scripts to detect unpinned actions and generate SHA-pinned versions.
Timeline:
v3 tag to point to backdoored commitImpact: Multi-organization breach. Secrets, credentials, and source code compromised across hundreds of repositories.
Prevention: SHA pinning. Workflows continue using verified commit. Dependabot flags the tag update for review.
Timeline:
Impact: Supply chain compromise affecting downstream users. Deployment credentials stolen, production systems compromised.
Prevention: SHA pinning with Dependabot review. Team reviews changelog and diff before approving SHA update.
Timeline:
actions/check0ut (zero instead of 'o')Impact: Developer typo leads to cloud account compromise. Thousands in cloud costs, potential data breach.
Prevention: SHA pinning forces explicit review. Full action path visible in security review. Allowlisting blocks unknown actions.
See the full implementation guide in the source documentation.
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
The Problem: Tags are mutable. Maintainers can update v4 to point to any commit. You have no guarantee the code hasn't changed since you tested it.
Attack Vector: Compromised maintainer moves tag to malicious commit. Every workflow using that tag now executes attacker code.
# actions/checkout v4.1.1
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
# actions/setup-node v3.8.1
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d
The Defense: SHA-256 commit hashes are immutable. The code at that hash cannot change. You're pinning to exact, verified code.
Comment Strategy: Include the semantic version in a comment so humans know what SHA represents.
See examples.md for code examples.
See reference.md for complete documentation.
documentation
Workload Identity Federation implementation guide. GKE setup, IAM bindings, ServiceAccount configuration, migration from service account keys, and troubleshooting patterns.
development
Secure GitHub Actions trigger patterns for pull requests, forks, and reusable workflows. Preventing privilege escalation and code injection through trigger misconfiguration.
development
Structured framework for evaluating GitHub Actions security before adoption. Trust tiers, risk assessment checklist, and decision tree for action evaluation.
testing
Securely store GitHub App credentials across different environments. GitHub Actions secrets, external CI, Kubernetes, and automated rotation patterns.