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
