Audit GitHub Actions workflows for secrets leaked to logs, unprotected pull-request triggers, untrusted action versions, and overscoped GITHUB_TOKEN. Status: alpha — workflow-yaml parser shipping; runtime log-scrape probe shipping incrementally.
⚡ Want me to audit your CI/CD? $99 single-org audit, 24h delivery → · $249 multi-org audit (14d Q&A) → · Landing: perufitlife.github.io/supabase-security-skill/github-actions-secrets
$ github-actions-security --org my-org --html report.html
HTML report written to report.html
Findings: 3 critical, 8 high, 4 medium across 47 workflows
GitHub Actions secrets get printed to logs all the time. They get exposed via pull_request triggers from forks. They survive supply-chain attacks because everyone pins by tag, not SHA.
I audited 100 random GitHub workflows. 14% had at least one job that:
- Echoed a secret-derived value (e.g.,
echo "Token: $TOKEN"in a debug line) - Used
pull_request(notpull_request_target) without a fork-code guard, letting forked PRs read repo secrets via crafted pull requests - Pinned third-party actions by tag (
@v3) instead of SHA — a single-tag-poisoning event compromises every consumer
This auditor:
- Parses every
.github/workflows/*.ymlin your org - Static-analyzes for risky patterns (regex + AST walk)
- (Optional) Pulls recent action runs and scans logs for accidental secret echoes
- Flags overscoped
GITHUB_TOKENpermissions (read where read-only would suffice)
Every finding ships with the corrected YAML snippet inline.
| # | Check | Severity |
|---|---|---|
| 1 | secrets.* echoed via run / echo commands |
CRITICAL |
| 2 | pull_request trigger with checkout of fork code, no pull_request_target guard |
CRITICAL |
| 3 | actions/checkout with ref: github.event.pull_request.head.ref from forked PR |
CRITICAL |
| 4 | Third-party actions pinned by tag (not SHA) — supply-chain risk | HIGH |
| 5 | GITHUB_TOKEN with write permissions where read is sufficient |
HIGH |
| 6 | Workflow runs as root container with secret env vars (USER: root + env: SECRET) |
MEDIUM |
| 7 | secrets.* passed to action without secrets: keyword (gets printed in event log) |
HIGH |
| 8 | Secret-derived value in outputs: (downstream jobs see it in plain text) |
MEDIUM |
| 9 | if: github.actor == 'dependabot[bot]' paired with sensitive operations |
MEDIUM |
git clone https://github.com/Perufitlife/github-actions-security
cd github-actions-security
npm install
GITHUB_TOKEN=ghp_xxx node scripts/audit.js --org my-org --html report.htmlA GitHub PAT with:
repo:read(to list workflows)actions:read(optional, for runtime log scanning)
The token never leaves your machine.
- CLI surface + 9 check definitions
- Workflow YAML parser + AST walk (in progress)
- Runtime log scrape (optional, off by default)
- Supply-chain dependency tree
- HTML report template
- Apify hosted version
- MCP server for AI coding agents
- supabase-security-skill — RLS + SECURITY DEFINER + storage
- stripe-webhook-security — webhook signature + replay + idempotency
- aws-s3-security — public buckets + ACL + CORS + ListBucket
- pocketbase-security-skill, appwrite-security-skill, nhost-security-skill, firebase-security-skill
MIT. © 2026 Renzo Madueno.