| name | github-actions-supply-chain |
| description | Use when reviewing GitHub Actions workflow files for security issues, hardening CI pipelines, or assessing supply chain risk in a repository that uses GitHub Actions |
GitHub Actions Supply Chain Assessment
Overview
GitHub Actions workflows run arbitrary code with access to repository secrets and a GITHUB_TOKEN. A compromised or repointed action is indistinguishable from a legitimate one — the attack lands silently in your CI logs. This skill provides a structured checklist for assessing and hardening Actions supply chain risk.
Never rely on your knowledge of whether an action version is "safe". Run the checklist.
Assessment Checklist
Work through every workflow file in .github/workflows/. For each file:
SHA Pinning
Why tags are unsafe
A mutable tag (@v4) can be silently repointed to a different commit by anyone with push access to that repository — including an attacker who has compromised the maintainer's account. The tj-actions/changed-files incident (March 2025) demonstrated this at scale: a compromised action exfiltrated secrets from thousands of repositories before the tag was corrected.
How to find the SHA for an action version
gh api repos/actions/checkout/git/ref/refs/tags/v4 --jq '.object.sha'
gh api repos/actions/checkout/git/tags/<sha-from-above> --jq '.object.sha'
Alternatively, browse to https://github.com/<owner>/<repo>/releases/tag/<version> and copy the full commit SHA from the "Assets" section or the commit link.
Pinned reference format
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
Always leave the tag as a comment so humans can understand what version is pinned.
First-party vs third-party risk
| Action namespace | Risk level | Rationale |
|---|
actions/* | Medium | GitHub-owned; GitHub employee accounts can be compromised |
github/* | Medium | Same as above |
| Any other owner | High | No privileged relationship; pin and verify provenance |
Permissions Scoping
Without an explicit permissions: block, a workflow inherits the repository's default token permissions — which may include write access to contents, pull requests, or packages.
Minimal read-only workflow (most CI workflows)
permissions:
contents: read
Workflow that needs to post PR comments
permissions:
contents: read
pull-requests: write
Workflow that uploads release artifacts
permissions:
contents: write
Apply the permissions: block at the job level when different jobs need different scopes. Apply at the workflow level when all jobs share the same minimal scope.
Dependabot for Actions
A dependabot.yml file tells GitHub to open PRs when pinned action SHAs fall behind the latest release. Without this, pinned SHAs become stale and fall behind security patches.
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
actions:
patterns:
- "*"
Add a separate package-ecosystem entry for each language's dependency manager (gomod, maven, etc.) as needed.
Report Format
After completing the checklist, produce a findings table:
## Supply Chain Assessment — .github/workflows/
| File | Finding | Severity | Fix |
| --- | --- | --- | --- |
| lint.yml | `DavidAnson/markdownlint-cli2-action@v16` not SHA-pinned | High | Pin to commit SHA |
| go-tests.yml | No `permissions:` block | Medium | Add `permissions: contents: read` |
| All files | No dependabot.yml for actions | Medium | Add `.github/dependabot.yml` |
Severity guide:
- High — third-party action not SHA-pinned;
pull_request_target misuse; script injection
- Medium — first-party action not SHA-pinned; no
permissions: block; no dependabot
- Low — informational (e.g. runner version, minor hygiene)
Common Mistakes
- Pinning the tag object SHA instead of the commit SHA — some tags point to tag objects, not commits. Always dereference to the underlying commit SHA.
- Pinning without a comment — a bare SHA is unreadable. Always comment the human-readable version alongside.
- Pinning but not enabling Dependabot — a pinned SHA that never updates is worse than a mutable tag once a CVE lands in the pinned version.
- Scoping permissions at workflow level when jobs have different needs — use job-level
permissions: for finer control.