Guides

Agent workflow review

Security review for GitHub Actions workflows that run coding agents — untrusted triggers with secrets, issue and PR text in prompts or shells, pull_request_target with fork code, unrestricted agent tools, broad permissions.

Running a coding agent in CI hands it your API keys and a repository token. In 2026 a pull-request title was enough to make several agent actions leak their keys, and an issue title led to stolen npm credentials. The pattern is always the same: a trigger anyone can fire, untrusted text in the prompt, broad tools, secrets in the same job.

What is checked

FindingSeverityWhy
An agent runs on pull_request_target, issue_comment, issues, discussion* or workflow_run with secrets and no author checkcriticalanyone can steer an agent that holds your keys
pull_request_target checks out the pull request headcriticalfork code runs with your secrets and a write token
issue / PR / comment / review text (${{ github.event.issue.title }}, .body, github.head_ref…) inside a run: scripthighshell injection: a title like "; curl … | sh # executes
the same text inside the agent's prompthighprompt injection — the path behind the 2026 key leaks
--dangerously-skip-permissions, --yolo, Bash allowed without a command patternhigh on untrusted triggers, else mediumany instruction becomes any command
permissions: write-all, or no permissions: blockhigh / mediumthe token can push code and edit workflows
the agent action is referenced by tag (@v1), not a commit SHAmediuma moved tag changes what runs with your keys
secrets: inheritmediumevery repository secret is passed on
an agent on an untrusted trigger without secrets or author checkmediumquota and write token are still exposed

Recognized agents: Claude Code (claude-code-action, claude -p), Gemini CLI (run-gemini-cli, gemini -p), Codex (codex-action, codex exec), OpenCode, Cline, Aider, Copilot agent.

The safe pattern

yaml
on:
  issue_comment:
    types: [created]
permissions:
  contents: read
  pull-requests: write
jobs:
  claude:
    if: contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: anthropics/claude-code-action@<full-commit-sha> # vX.Y.Z
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          claude_args: --allowedTools "Bash(npm test),Read,Grep"

Let the agent read the issue or PR through its tools as data instead of pasting the text into the prompt.

Run it

  • Agents: codexguild_scan_workflows on the local MCP server reviews .github/workflows/*.yml offline — nothing is uploaded.
  • Web: paste a workflow at /lint?mode=workflow.
  • REST: POST https://api.codexguild.com/v1/lint/workflow { "content": "…" } (public, nothing stored).