Skip to content

Your Docs Provide an Unsafe Expression #18763

@harrisoncramer

Description

@harrisoncramer

In your examples, this expression is given (https://github.github.com/gh-aw/guides/deterministic-agentic-patterns/#post-processing-pattern):

format-and-notify:
      description: "Format and post review"
      runs-on: ubuntu-latest
      inputs:
        summary: {required: true, type: string}
      steps:
        - run: |
            echo "##  AI Code Review\n\n${{ inputs.summary }}" > /tmp/report.md
            gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/report.md
          env:
            GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

But if you try to actually compile this it fails:

The following expressions are used directly in shell commands, which enables template injection attacks:

  workflow inputs context (1 occurrence(s)):
    - ${{ inputs.summary }}
      in: echo "##  AI Code Review\n\n${{ inputs.summary }}" > /tmp/report.md

  github.event context (1 occurrence(s)):
    - ${{ github.event.pull_request.number }}
      in: gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/report.md

Security Risk:
  When expressions are used directly in shell commands, an attacker can inject
  malicious code through user-controlled inputs (issue titles, PR descriptions,
  comments, etc.) to execute arbitrary commands, steal secrets, or modify the repository.

Safe Pattern - Use environment variables instead:
  env:
    MY_VALUE: ${{ github.event.issue.title }}
  run: |
    echo "Title: $MY_VALUE"

Unsafe Pattern - Do NOT use expressions directly:
  run: |
    echo "Title: ${{ github.event.issue.title }}"  # UNSAFE!

References:
  - https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions
  - https://docs.zizmor.sh/audits/#template-injection
  - scratchpad/template-injection-prevention.md

I'm trying to provide inputs and it's confusing when your own docs contradict the way the compiler step works.

For instance, it's unclear to me if this is the expected way to do this? Will this work?

# When it's done generating the summary, the agent will use a "safe output" (see https://github.github.io/gh-aw/reference/custom-safe-outputs/)
safe-outputs:
  jobs:
    send-slack-summary:
      description: "Sends notification to Slack"
      runs-on: ubuntu-latest
      inputs:
        summary:
            required: true
            type: string
      steps:
        - run: ./mise/tasks/github/slack/release-summary --tag "$TAG" --summary "$SUMMARY" --webhook-url "$SLACK_RELEASES_WEBHOOK_URL"
          env:
            TAG: ${{ inputs.release_tag }}
            SLACK_RELEASES_WEBHOOK_URL: ${{ secrets.SLACK_RELEASES_WEBHOOK_URL }}
            SUMMARY: ${{ inputs.summary }}

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions