fix: restore copilot-requests scope and apply least-privilege permissions - #78
Merged
Conversation
This was referenced Aug 20, 2026
austenstone
force-pushed
the
austenstone-workflow-hardening
branch
from
August 20, 2026 16:59
f67fea8 to
ab0a606
Compare
…ions copilot-labeler.yml declared explicit permissions but omitted copilot-requests: write, so every run failed with "Authentication failed". Because fail-on-error defaults to false, the job still reported success and the workflow has been silently doing nothing. The action now emits a warning on any non-zero exit so this class of failure is visible. The other eight workflows had no permissions block at all, inheriting the permissive repo default (including contents: write). They worked by accident. Each now declares only what it needs, with copilot-requests: write included so the explicit block does not reproduce the labeler bug. copilot-ci-fix.yml used write-all "because we need workflow: write", but workflows is not a GITHUB_TOKEN permission scope, so write-all never granted it. It was maximum privilege for no benefit, on a workflow_run trigger that fires for every failed workflow. Also adds timeout-minutes to all 36 jobs, SHA-pins the only third-party action (dependabot/fetch-metadata v2 -> v3.1.0), and moves checkout to v7, which blocks fork-PR checkout under workflow_run and pull_request_target. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The prompt read github.event.issue.*, which is null on pull_request, so every PR triage ran against a blank title and body and an empty issue number. Fall back to github.event.pull_request.* and raise pull-requests to write, since labeling a PR is now actually reachable.
austenstone
force-pushed
the
austenstone-workflow-hardening
branch
from
August 20, 2026 17:22
e0ad9c5 to
be60c7b
Compare
This was referenced Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug this started from
copilot-labeler.ymlhas been silently broken in production. It declares explicit permissions but omitscopilot-requests: write:Its most recent run is green. The log is not:
The job passed because
fail-on-errordefaults tofalseand the action exited silently on a non-zero exit code. Green check, zero work done, no signal.Fixes:
copilot-requests: writeto the labeler.::warning::on any non-zero exit whenfail-on-error: false. The job still passes (no behavior break), but the failure becomes visible. This is what would have caught the labeler months ago.Why the other workflows "worked"
The other 8 workflows had no
permissions:block at all, so they inherited the permissive repo default, which happens to includeCopilotRequests: writeandContents: write. They worked by accident, at maximum privilege.That is also the trap: adding a least-privilege block without
copilot-requests: writesilently breaks the workflow. Every block added here includes it.copilot-requests: write)contents: read,actions: read,issues: writecontents: write,pull-requests: write,actions: readcontents: read,issues: write,pull-requests: writecontents: read,pull-requests: writecontents: read,pull-requests: writecontents: read,issues: writecontents: read,issues: write,security-events: readcontents: read,issues: writewrite-allwas pointless and dangerousworkflowsis not aGITHUB_TOKENpermission scope. Pushing changes under.github/workflows/requires a PAT with theworkflowscope, full stop. Sowrite-allnever granted the thing the comment says it was there for. It was maximum privilege for zero benefit, on aworkflow_runtrigger firing for every failed workflow in the repo.This is confirmed by the auto-generated fix PRs themselves, which report "The CI repair token lacks the workflows permission."
Now scoped to what it actually uses.
Also
timeout-minuteson all 36 jobs. Previously only the labeler had one, on workflows running open-ended agentic jobs that can otherwise hang to the 6h default.dependabot/fetch-metadata@v2to@25dd0e34f4fe68f24cc83900b1fe3fe149efef98(v3.1.0). Everything else is first-partyactions/*.actions/checkoutto v7 everywhere (was a v5/v6 split). v7 blocks fork-PR checkout underworkflow_runandpull_request_target, which is directly relevant tocopilot-ci-fix.yml. Supersedes Bump actions/checkout from 5 to 6 #50.Added after initial review
copilot-ci-fix.yml'sworkflow_runtrigger fromworkflows: ['*']to the CI workflow. It was firing the write-privileged repair bot on every workflow completion in the repo — 48 of the last 100 runs were no-op guard skips. Reported independently by the auto-generated optimization report in Workflow Optimization Report — 2026-08-01 #76.