Secure your GitHub Actions workflows against supply chain attacks. Detects unpinned actions (the tj-actions attack vector), dangerous permissions, and script injection β all in one scan.
After the tj-actions supply chain attack compromised 23k+ repositories, teams realized: GitHub Actions workflows are a security blind spot. This tool fixes that.
- Unpinned actions β Actions without commit SHA pinning are vulnerable to malicious tag rewrites. Example:
- uses: actions/checkout@v3 # π΄ VULNERABLE β pinned to tag, not SHA - uses: actions/checkout@a81bbbf8298c0fa03ea29cdc473d45aca646fdde3 # β SAFE
- Dangerous permissions β Overly broad permissions (
permissions: write-allor missing explicit deny). Example:permissions: contents: write issues: write pull-requests: write # Is this really needed?
- Script injection β Run steps with unsanitized inputs from PRs. Example:
- run: echo ${{ github.event.pull_request.title }} # π΄ VULNERABLE
- Missing
permissionsblock (defaults to broad access) - Secrets passed as environment variables (use
secrets:instead) - Suspicious use of
github.tokenin external actions
- Deprecated action versions
- Missing checkout step before build
- Unreviewed third-party actions
Scan any public repo with a single HTTP call:
# Scan a GitHub repo
curl -X POST http://89.167.76.186:8000/scan \
-H 'Content-Type: application/json' \
-d '{"repo": "owner/repo"}'
# Scan raw workflow YAML
curl -X POST http://89.167.76.186:8000/scan \
-H 'Content-Type: application/json' \
-d '{"yaml": "name: test\non: push\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4"}'Response:
{
"files_scanned": 19,
"issues": [...],
"summary": {"total": 105, "critical": 85, "high": 20, "medium": 0, "low": 0}
}Free, no auth required.
Option 1: Homebrew (macOS & Linux)
brew tap indoor47/homebrew-gh-workflow-hardener
brew install gh-workflow-hardenerOption 2: From source
git clone https://github.com/indoor47/gh-workflow-hardener
cd gh-workflow-hardener
pip install .Add to .github/workflows/security-check.yml:
name: Workflow Security Check
on:
pull_request:
paths:
- '.github/workflows/**'
push:
branches:
- main
paths:
- '.github/workflows/**'
permissions:
contents: read
jobs:
hardener:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@a81bbbf8298c0fa03ea29cdc473d45aca646fdde3
- uses: indoor47/gh-workflow-hardener@bd2a0a1f06117de11bd7da6b62b80087596f569f # v1.1.0
with:
fail_on: critical# Scan current repository (expects .github/workflows/)
gh-workflow-hardener
# Scan a specific repository
gh-workflow-hardener /path/to/repo
# Output as JSON (for CI integration)
gh-workflow-hardener --format json
# Output as SARIF (for GitHub Code Scanning)
gh-workflow-hardener --format sarif
# Auto-fix unpinned actions by resolving tags to commit SHAs
gh-workflow-hardener --fix --token $GITHUB_TOKEN
# Fail only on critical + high severity issues
gh-workflow-hardener --fail-on highGitHub Actions Workflow Security Report
=========================================
.github/workflows/build.yml
[CRITICAL] Line 12: Unpinned action: actions/checkout@v3
β Pin to: actions/checkout@a81bbbf8298c0fa03ea29cdc473d45aca646fdde3
[HIGH] Line 45: Overly broad permissions
β Found: permissions: write-all
β Fix: Specify only needed permissions
.github/workflows/deploy.yml
[MEDIUM] Line 8: Secrets passed as env var
β Use: secrets: { AWS_KEY: ${{ secrets.AWS_KEY }} }
=========================================
Issues: 3 critical, 1 high, 1 medium
Status: FAIL (--fail-on critical)
We scanned workflows from 8 popular open-source repositories (Feb 2026):
| Repository | Stars | Workflows | Findings | Score |
|---|---|---|---|---|
| facebook/react | 236k | 20 | 197 | 0/100 |
| vercel/next.js | 132k | 25 | 143 | 0/100 |
| langchain-ai/langchain | 105k | 16 | 89 | 0/100 |
| django/django | 83k | 16 | 56 | 0/100 |
| fastapi/fastapi | 82k | 19 | 121 | 0/100 |
| microsoft/vscode | 170k | 12 | 66 | 0/100 |
| astral-sh/ruff | 40k | 19 | 8 | 40/100 |
| pallets/flask | 69k | 4 | 6 | 55/100 |
686 findings across 131 workflow files. Zero false positives.
73% of findings are unpinned action references, the exact vulnerability exploited in the tj-actions attack. Repos that already pin to SHAs (ruff, flask) score dramatically higher.
The tj-actions attack showed that a single compromised action can:
- Steal repository secrets and tokens
- Modify code in pull requests
- Exfiltrate source code
- Inject malicious dependencies
By pinning actions to specific commit SHAs and validating permissions, you eliminate entire attack vectors.
| Input | Default | Description |
|---|---|---|
fail_on |
critical |
Exit with error if findings at this severity or above. Options: critical, high, medium, low, none |
format |
text |
Output format: text, json, markdown, sarif |
fix |
false |
Auto-fix unpinned actions by pinning to commit SHAs |
github_token |
${{ github.token }} |
GitHub token for SHA resolution (needed for auto-fix) |
The CLI requires only pyyaml and click. No heavy scanning frameworks, no bloat. Fast. Auditable.
Prefer catching issues as you type? The companion GitHub Workflow Hardener VS Code extension runs the same checks inline while you edit .github/workflows/ files β no CLI needed.
Found a false positive? Have a better detection rule? Open an issue or PR.
MIT