Skip to content

Fork PR review events leave labels stale until the hourly sweep #2046

Open

Description

@edelauna

Problem

A review on a fork PR does not update its labels for up to one hour. Examples: #1293 and #1705.

Context

GitHub gives pull_request_review runs for fork PRs a read-only GITHUB_TOKEN, even when the workflow asks for write permissions. The workflow already detects this (isReadOnlyRun && isForkPR(pr)), computes the right label, and then skips the write. The log for #1705 shows this:

PR #1705: CI passing, reviews=2, coderabbit=APPROVED, maintainer=pending, blockers=1 → awaiting-author
PR #1705: fork PR on a read-only run — skipping label mutation

The label changes only when a trusted event runs: a CodeRabbit comment update, a workflow_run of a listed workflow, the hourly sweep, or a push to main. For a maintainer who requests changes on a fork PR, this can be up to 60 minutes. Most PRs in this repo come from forks.

Research

  • GitHub documents that fork PRs always get a read-only token on pull_request_review. There is no setting to raise it. Community discussion #26651, #55940.
  • GitHub's guidance for labeling fork PRs is a privileged trigger that does not run PR code. Securely using pull_request_target. There is no pull_request_review_target event, so pull_request_target cannot cover review events.
  • GitHub Security Lab recommends a two-workflow pattern: an unprivileged workflow handles the untrusted event, and a privileged workflow runs on workflow_run. Preventing pwn requests, Secure use reference.
  • The risk to avoid in the privileged workflow is a checkout of PR code, or use of untrusted artifacts. This workflow does neither. It reads only the PR number, and it fetches all PR data with the API. A workflow_run follow-up keeps that property if it takes no artifact input.

Proposed fix

  1. Add this workflow's own name to the workflow_run.workflows list.
  2. In the resolve job, accept a workflow_run of this workflow only when github.event.workflow_run.event == 'pull_request_review'. This stops a loop: a run triggered by workflow_run has the event workflow_run, so the next level is filtered out.
  3. Resolve the PR with the existing head SHA fallback. For fork PRs workflow_run.pull_requests is empty, and the workflow already handles this.
  4. Take no artifact input. Do not check out code.
  5. Add tests: a fork review run is followed by a trusted run that writes labels; a workflow_run of a non-review run is ignored; the loop guard holds.

Alternative: shorten the sweep from hourly to every 15 minutes. It is simple, but it costs 4 times the API calls against a shared 1,000 requests per hour pool. I do not recommend it.

File: .github/workflows/label-pr-review-state.yml. Tests: src/services/__tests__/pr-review-state-workflow.test.ts.

Related: #1293, #1705, #1739.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions