IntentiAI

Issue or PR text interpolated into CI

Issue or PR text interpolated into a CI script or an AI action.

Check workflow-prompt-injectionShip CheckGitHub Actions and GitLab CI YAML

Why it matters

Agents write GitHub Actions the way the examples read: drop ${{ github.event.issue.title }} straight into a run: script or pass the issue body to an AI action as its prompt. It works on the first test issue, and the syntax looks like a safe variable.

It is not a variable. GitHub pastes the text into the script before the shell runs, so anyone who can open an issue or pull request can put a backtick or $( ) in the title and run commands on your runner with its secrets and token. Passed to an AI step, the same text becomes instructions the agent may follow with the job's credentials.

Example

 jobs:
   triage:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      - run: echo "New issue opened"
+      - name: Label issue
+        run: |
+          echo "Triaging: ${{ github.event.issue.title }}"
+          ./scripts/label.sh "${{ github.event.issue.body }}"

What it detects

  • ${{ }} expressions carrying outsider-controlled context (issue, pull request, comment and discussion text, fork branch names, github.head_ref, commit messages) inside a run: block, or passed through with: to an action whose name suggests an AI step (claude, anthropic, openai, gpt, gemini, copilot, llm, agent).
  • github.event.inputs values, unless the workflow is triggered only by workflow_dispatch.
  • The same expression inside env:, if:, name:, or a with: name, title or label is the documented safe form and is not flagged.
  • curl or wget piped into a shell or Python inside run:, and pull_request_target workflows that check out the pull request's head ref or sha.
  • GitLab CI equivalents such as $CI_COMMIT_MESSAGE, $CI_MERGE_REQUEST_TITLE and the source branch name inside script blocks.

What to do when it fires

Move the value into an env: entry on the step (for example ISSUE_TITLE: ${{ github.event.issue.title }}) and reference "$ISSUE_TITLE" in the script. The shell then receives it as data. This is GitHub's own documented fix.

For AI actions, avoid handing raw event text to a step that can run tools, and restrict that job's token to the minimum permissions. Replace curl-pipe-shell installs with a pinned version and a checksum, and do not build or run fork code under pull_request_target.

Issue or PR text interpolated into CI: AI code review check | IntentiAI