Skillshub gha-security-review
GitHub Actions security review for workflow exploitation vulnerabilities. Use when asked to "review GitHub Actions", "audit workflows", "check CI security", "GHA security", "workflow security review", or review .github/workflows/ for pwn requests, expression injection, credential theft, and supply chain attacks. Exploitation-focused with concrete PoC scenarios.
git clone https://github.com/ComeOnOliver/skillshub
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/getsentry/skills/gha-security-review" ~/.claude/skills/comeonoliver-skillshub-gha-security-review && rm -rf "$T"
skills/getsentry/skills/gha-security-review/SKILL.mdGitHub Actions Security Review
Find exploitable vulnerabilities in GitHub Actions workflows. Every finding MUST include a concrete exploitation scenario — if you can't build the attack, don't report it.
This skill encodes attack patterns from real GitHub Actions exploits — not generic CI/CD theory.
Scope
Review the workflows provided (file, diff, or repo). Research the codebase as needed to trace complete attack paths before reporting.
Files to Review
— all workflow definitions.github/workflows/*.yml
/action.yml
— composite actions in the repoaction.yaml
— local reusable actions.github/actions/*/action.yml- Config files loaded by workflows:
,CLAUDE.md
,AGENTS.md
, shell scripts underMakefile.github/
Out of Scope
- Workflows in other repositories (only note the dependency)
- GitHub App installation permissions (note if relevant)
Threat Model
Only report vulnerabilities exploitable by an external attacker — someone without write access to the repository. The attacker can open PRs from forks, create issues, and post comments. They cannot push to branches, trigger
workflow_dispatch, or trigger manual workflows.
Do not flag vulnerabilities that require write access to exploit:
input injection — requires write access to triggerworkflow_dispatch- Expression injection in
-only workflows on protected branchespush
input injection where all callers are internalworkflow_call- Secrets in
/workflow_dispatch
-only workflowsschedule
Confidence
Report only HIGH and MEDIUM confidence findings. Do not report theoretical issues.
| Confidence | Criteria | Action |
|---|---|---|
| HIGH | Traced the full attack path, confirmed exploitable | Report with exploitation scenario and fix |
| MEDIUM | Attack path partially confirmed, uncertain link | Report as needs verification |
| LOW | Theoretical or mitigated elsewhere | Do not report |
For each HIGH finding, provide all five elements:
- Entry point — How does the attacker get in? (fork PR, issue comment, branch name, etc.)
- Payload — What does the attacker send? (actual code/YAML/input)
- Execution mechanism — How does the payload run? (expression expansion, checkout + script, etc.)
- Impact — What does the attacker gain? (token theft, code execution, repo write access)
- PoC sketch — Concrete steps an attacker would follow
If you cannot construct all five, report as MEDIUM (needs verification).
Step 1: Classify Triggers and Load References
For each workflow, identify triggers and load the appropriate reference:
| Trigger / Pattern | Load Reference |
|---|---|
| |
with command parsing | |
in blocks | |
| PATs / deploy keys / elevated credentials | |
| Checkout PR code + config file loading | |
| Third-party actions (especially unpinned) | |
block or secrets usage | |
| Self-hosted runners, cache/artifact usage | |
| Any confirmed finding | |
Load references selectively — only what's relevant to the triggers found.
Step 2: Check for Vulnerability Classes
Check 1: Pwn Request
Does the workflow use
pull_request_target AND check out fork code?
- Look for
withactions/checkout
pointing to PR headref: - Look for local actions (
) that would come from the fork./.github/actions/ - Check if any
step executes code from the checked-out PRrun:
Check 2: Expression Injection
Are
${{ }} expressions used inside run: blocks in externally-triggerable workflows?
- Map every
expression in every${{ }}
steprun: - Confirm the value is attacker-controlled (PR title, branch name, comment body — not numeric IDs, SHAs, or repository names)
- Confirm the expression is in a
block, notrun:
,if:
, or job-levelwith:env:
Check 3: Unauthorized Command Execution
Does an
issue_comment-triggered workflow execute commands without authorization?
- Is there an
check?author_association - Can any GitHub user trigger the command?
- Does the command handler also use injectable expressions?
Check 4: Credential Escalation
Are elevated credentials (PATs, deploy keys) accessible to untrusted code?
- What's the blast radius of each secret?
- Could a compromised workflow steal long-lived tokens?
Check 5: Config File Poisoning
Does the workflow load configuration from PR-supplied files?
- AI agent instructions:
,CLAUDE.md
,AGENTS.md.cursorrules - Build configuration:
, shell scriptsMakefile
Check 6: Supply Chain
Are third-party actions securely pinned?
Check 7: Permissions and Secrets
Are workflow permissions minimal? Are secrets properly scoped?
Check 8: Runner Infrastructure
Are self-hosted runners, caches, or artifacts used securely?
Safe Patterns (Do Not Flag)
Before reporting, check if the pattern is actually safe:
| Pattern | Why Safe |
|---|---|
WITHOUT checkout of fork code | Never executes attacker code |
in | Numeric only — not injectable |
/ | Repo owner controls this |
| Not an expression injection vector |
in conditions | Evaluated by Actions runtime, not shell |
in inputs | Passed as string parameters, not shell-evaluated |
| Actions pinned to full SHA | Immutable reference |
trigger (not ) | Runs in fork context with read-only token |
Any expression in // to protected branches | Requires write access — outside threat model |
Key distinction:
${{ }} is dangerous in run: blocks (shell expansion) but safe in if:, with:, and env: at the job/step level (Actions runtime evaluation).
Step 3: Validate Before Reporting
Before including any finding, read the actual workflow YAML and trace the complete attack path:
- Read the full workflow — don't rely on grep output alone
- Trace the trigger — confirm the event and check
conditions that gate executionif: - Trace the expression/checkout — confirm it's in a
block or actually references fork coderun: - Confirm attacker control — verify the value maps to something an external attacker sets
- Check existing mitigations — env var wrapping, author_association checks, restricted permissions, SHA pinning
If any link is broken, mark MEDIUM (needs verification) or drop the finding.
If no checks produced a finding, report zero findings. Do not invent issues.
Step 4: Report Findings
## GitHub Actions Security Review ### Findings #### [GHA-001] [Title] (Severity: Critical/High/Medium) - **Workflow**: `.github/workflows/release.yml:15` - **Trigger**: `pull_request_target` - **Confidence**: HIGH — confirmed through attack path tracing - **Exploitation Scenario**: 1. [Step-by-step attack] - **Impact**: [What attacker gains] - **Fix**: [Code that fixes the issue] ### Needs Verification [MEDIUM confidence items with explanation of what to verify] ### Reviewed and Cleared [Workflows reviewed and confirmed safe]
If no findings: "No exploitable vulnerabilities identified. All workflows reviewed and cleared."