liveBook · Manning

Shell injection attack

As explained in GitHub Actions in Action: Continuous integration and delivery for DevOps

A shell injection attack is an attack in which untrusted input causes unintended shell commands to execute.

A shell injection attack occurs when untrusted input makes a workflow execute unintended shell commands. In GitHub Actions, risky input can include an issue title, branch name, or pull-request body; direct use in a run script can turn data into executable shell content. section 6.2 section 10.2

Keep event data out of script generation

Pass event data through an environment variable and quote it in the script, rather than embedding an expression directly in run. The injected payload shown after a crafted title can issue curl with the GITHUB_TOKEN; the environment-variable form treats the title as a string for echoing. section 10.2 section 10.2 section 10.2

Quoting matters even beyond an overt attack. An unquoted variable undergoes word splitting, so whitespace or special characters become separate arguments. Quote variables in scripts to avoid unexpected behavior and related security problems. section 10.2

Reduce exposure through workflow checks and runner controls:

  • Run ActionLinter or actionlint in CI so the workflow fails on detected shell-injection vulnerabilities and reports the offending details. section 6.2 section 10.2
  • Use CodeQL queries early in the development lifecycle to detect harmful patterns across workflows, actions, and written source code. section 10.2
  • Do not run untrusted jobs on a self-hosted runner with private-network access. When necessary, use a single-use contained runner without network connections after stringent automated and manual checks. section 6.2
  • Gate sensitive jobs with pull-request approval, environment protection rules, custom checks, or branch restrictions. section 6.2
Listing 10.4 Action linting workflow to prevent known vulnerabilities
on:
  pullrequest: 
jobs:
  run-actionlint:
    runs-on: ubuntu-latest
    permissions:
      # Needed for the checkout action
      contents: read
      # Needed to annotate the files in a pull request with comments
      pull-requests: write
    steps: 
    # Checkout the source code to analyze.
    - uses: actions/checkout@v3 
    # Runs the actionlinter, which will fail on errors
    - uses: devops-actions/actionlint@c0ee017f8abef55d843a647cd737b87a1976eb69

This workflow installs actionlint after checkout and grants only the listed permissions, making linting an enforceable pull-request check. section 10.2

How GitHub Actions in Action: Continuous integration and delivery for DevOps covers it

How deep it goes
Explains unsafe expansion, quoting, word splitting, environment-variable handling, linting, and runner containment; defers CodeQL details.
Examples
Uses issue titles, branch names, pull-request bodies, a malicious curl payload, quoted environment variables, and an actionlint workflow.
What it assumes
Assumes familiarity with GitHub Actions workflows and shell variables while defining word splitting and quoting behavior.
What you can do after
Apply it via ActionLinter, CodeQL, quoted environment variables, and protected ephemeral runners to reduce workflow injection exposure.
How it connects
Connects shell injection to GitHub Actions, self-hosted runners, environment protection rules, CodeQL, ActionLinter, and word splitting.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of GitHub Actions in Action: Continuous integration and delivery for DevOps. The section links point to the parts of the book it draws on.