fix(ci): let the PR labeler actually write labels - #566
Conversation
📝 WalkthroughWalkthroughThe pull request labeler workflow now requests ChangesPull request labeler permissions
Estimated code review effort: 1 (Trivial) | ~3 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/pr-labeler.yml:
- Around line 20-27: Move the pull-requests: write permission from
workflow-level permissions into only the job invoking addLabels, removeLabel,
and createLabel. Keep unrelated jobs read-only or without this permission,
preserving the labeler job’s ability to mutate labels.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 37b5db43-0c11-464d-a2ec-415ff96c79aa
📒 Files selected for processing (1)
.github/workflows/pr-labeler.yml
| # pulls.get only needs read, but the issues label endpoints are shared with | ||
| # pull requests: adding or removing a label on a PR number is rejected with | ||
| # "Resource not accessible by integration" unless the token also carries | ||
| # pull_requests=write (the API reports `issues=write; pull_requests=write` | ||
| # in x-accepted-github-permissions). Read-only here silently worked while | ||
| # every run happened to be a no-op sync, and failed on the first PR that | ||
| # actually needed a label written. | ||
| pull-requests: write |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Scope pull-requests: write to the labeler job.
The write permission is required for the label mutations, but granting it at workflow scope gives every job and future step a token capable of modifying pull requests. Move this permission under only the job that calls addLabels, removeLabel, and createLabel, while keeping unrelated jobs read-only or without the permission.
Proposed fix
permissions:
- pull-requests: write
jobs:
<labeler-job>:
+ permissions:
+ pull-requests: write🧰 Tools
🪛 zizmor (1.26.1)
[error] 27-27: overly broad permissions (excessive-permissions): pull-requests: write is overly broad at the workflow level
(excessive-permissions)
[warning] 27-27: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment
(undocumented-permissions)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/pr-labeler.yml around lines 20 - 27, Move the
pull-requests: write permission from workflow-level permissions into only the
job invoking addLabels, removeLabel, and createLabel. Keep unrelated jobs
read-only or without this permission, preserving the labeler job’s ability to
mutate labels.
Sources: Path instructions, Linters/SAST tools
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9933017e0c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # in x-accepted-github-permissions). Read-only here silently worked while | ||
| # every run happened to be a no-op sync, and failed on the first PR that | ||
| # actually needed a label written. | ||
| pull-requests: write |
There was a problem hiding this comment.
Update the minimal-permissions regression test
Changing this permission to write leaves .github/scripts/pr-labeler.test.cjs:165-170 explicitly requiring pull-requests: read and rejecting pull-requests: write. The checked .github/workflows/issue-quality-tests.yml is path-triggered by this workflow and runs that test, so node --test .github/scripts/pr-labeler.test.cjs now fails. Update the regression test to expect the intentional write permission; otherwise this change cannot get green CI.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/scripts/pr-labeler.test.cjs:
- Around line 174-177: Update the workflow permission assertions in the relevant
test to inspect the effective top-level permissions map rather than matching
arbitrary YAML text. Parse the workflow or isolate its top-level permissions
block, then assert exactly contents: read, pull-requests: write, and issues:
write, while rejecting contents: write; do not rely on comments or job-level
permissions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 1e450641-fffc-4922-88d9-969075b4789f
📒 Files selected for processing (1)
.github/scripts/pr-labeler.test.cjs
| assert.match(workflow, /pull-requests:\s*write/); | ||
| // contents stays read — the labeler never pushes. | ||
| assert.match(workflow, /contents:\s*read/); | ||
| assert.doesNotMatch(workflow, /contents:\s*write/); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Assert the effective top-level permissions map, not arbitrary workflow text.
The unanchored regexes search the entire YAML document, so a comment or unrelated job-level block could satisfy pull-requests: write and contents: read without proving the labeler job receives those permissions. Conversely, an unrelated contents: write occurrence could fail the test. Parse the workflow or extract its top-level permissions block and assert the exact contract: contents: read, pull-requests: write, and issues: write, with no contents: write.
As per path instructions, .github/** is a security boundary and workflow permission changes require explicit security review.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/scripts/pr-labeler.test.cjs around lines 174 - 177, Update the
workflow permission assertions in the relevant test to inspect the effective
top-level permissions map rather than matching arbitrary YAML text. Parse the
workflow or isolate its top-level permissions block, then assert exactly
contents: read, pull-requests: write, and issues: write, while rejecting
contents: write; do not rely on comments or job-level permissions.
Source: Path instructions
* fix(ci): let the PR labeler actually write labels * test(ci): pin the labeler to the permissions the API demands
* fix(ci): let the PR labeler actually write labels * test(ci): pin the labeler to the permissions the API demands
Problem
The PR Labeler workflow fails with a 403 whenever it actually needs to write a label:
Observed on #565 (runs 30292169466 and 30292826165).
Cause
The job token was granted exactly what the workflow asked for:
But GitHub's own rejection names a wider requirement:
The issues label endpoints are shared with pull requests. Writing a label onto a
PR number needs
pull_requests=writeas well, sopull-requests: readcannotsatisfy
POST /issues/{number}/labelseven withissues: write.This stayed invisible because almost every run is a no-op: the title already
matches the current label,
planTypeLabelSyncreturns a skip, and no write isattempted. Runs on #512, #447, and #528 all "passed" this way. #565 was the
first PR in a while that genuinely needed a label applied, so it was the first
to surface the misconfiguration.
Fix
Raise
pull-requeststowritein the workflow's top-level permissions, with acomment recording why the read-only value looked sufficient but is not.
No script or logic change:
planTypeLabelSyncand the sticky human-overridebehaviour are untouched, so the labeler still refuses to overwrite a maintainer's
manual type label.
Note on scope
This is a workflow permission increase, so it falls under the security-review
boundary in
MAINTAINERS.md. The added scope is the minimum the API demands forthe write the workflow already performs; it grants no new capability beyond
label mutation on pull requests.
pull_request_targetworkflows load from the default branch (main), so thisdoes not take effect on live PRs until it is promoted there.
Verification
bun scripts/test.ts→ 5070 pass / 0 failbun run privacy:scan→ passSummary by CodeRabbit