Skip to content

[SECURITY] /format CI command allows arbitrary code execution via untrusted fork PRs #236

Description

@Tamcodes4

Description

.github/workflows/format-command.yml triggers on issue_comment with no authorization check on the commenter, then checks out the PR head's fork branch and runs npx prettier --write . inside a job holding contents: write and a live GITHUB_TOKEN, before auto-committing and pushing back to the base repo.

Steps to Reproduce

  1. Fork the repo and open a PR from the fork containing a malicious .prettierrc.js (Prettier configs are JS and execute on load, e.g. one that runs require('child_process').exec(...)).
  2. Comment /format on your own PR.
  3. The workflow checks out your fork's branch and runs your config with contents: write + GITHUB_TOKEN permissions, then commits the result back to the base repo.

Expected Behavior

Only maintainers/collaborators should be able to trigger a workflow that runs code from a PR branch with write access to the repo.

Actual Behavior

Any GitHub account, including the PR author on their own fork PR, can trigger this job. The if: condition only checks contains(github.event.comment.body, '/format'), with no check on github.event.comment.user.login or author_association.

Root Cause

if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/format')
permissions:
  contents: write
  pull-requests: write
  issues: write
steps:
  - uses: actions/checkout@v4
    with:
      repository: ${{ fromJSON(steps.pr.outputs.result).head.repo.full_name }}
      ref: ${{ fromJSON(steps.pr.outputs.result).head.ref }}
      token: ${{ secrets.GITHUB_TOKEN }}
  - run: npx prettier --write .
  - uses: stefanzweifel/git-auto-commit-action@v5

This is the classic "pwn request" anti-pattern: untrusted fork content is checked out and executed inside a job that holds write-scoped secrets.

Suggested Fix

Gate the job on commenter authorization:

if: |
  github.event.issue.pull_request != '' &&
  contains(github.event.comment.body, '/format') &&
  (github.event.comment.author_association == 'OWNER' ||
   github.event.comment.author_association == 'MEMBER' ||
   github.event.comment.author_association == 'COLLABORATOR')

Alternatively, replace this with a maintained slash-command dispatcher (e.g. peter-evans/slash-command-dispatch) that handles authorization correctly.

Affected Files

  • .github/workflows/format-command.yml

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions