Found while hardening validate-task.yml in #550, after CodeRabbit raised the missing block as a merge-risk note on #553.
The repository's default workflow permission is write, and can_approve_pull_request_reviews is true:
$ gh api repos/ptr727/NxWitness/actions/permissions/workflow
{"default_workflow_permissions": "write", "can_approve_pull_request_reviews": true}
So any job that declares no permissions: block runs with a read/write GITHUB_TOKEN. #550 closes that for validate-task.yml and for the two test-pull-request.yml jobs that own their own steps. These four remain:
| Workflow |
Jobs |
Permissions blocks |
build-base-images-task.yml |
1 |
0 |
build-docker-task.yml |
3 |
0 |
get-version-task.yml |
1 |
0 |
publish-plan-task.yml |
1 |
0 |
publish-release.yml declares one block across ten jobs, so most of its jobs are covered by neither their own declaration nor a workflow-level default.
Why this is a separate change
These sit on the publish path rather than the validation path, and least privilege there is not simply contents: read. A Docker build-and-push job needs registry credentials, get-version-task runs NBGV against history, and publish-plan-task reads the trigger context. Getting any of them wrong breaks publishing rather than failing a lint, and the failure would surface on a weekly scheduled run rather than on a pull request.
That is the opposite risk profile from the validation fix, which was proven safe by the hub's own copy running the identical actions under contents: read. So it wants its own change, with each job's required scope established from what its steps actually call, and ideally a dispatch run to prove the publish path still works before it is relied on.
Suggested approach
Establish the needed scope per job rather than applying contents: read broadly, then add a workflow-level default to publish-release.yml so a future job cannot silently inherit write. GOVERNANCE.md "Workflow YAML Conventions" states the rule ("Grant least privilege; a callee's extra scope is granted by the caller"), and WORKFLOW.md's D-guarantees describe what each job in the chain is for, which is the input to deciding each scope.
Found while hardening
validate-task.ymlin #550, after CodeRabbit raised the missing block as a merge-risk note on #553.The repository's default workflow permission is write, and
can_approve_pull_request_reviewsis true:So any job that declares no
permissions:block runs with a read/writeGITHUB_TOKEN. #550 closes that forvalidate-task.ymland for the twotest-pull-request.ymljobs that own their own steps. These four remain:build-base-images-task.ymlbuild-docker-task.ymlget-version-task.ymlpublish-plan-task.ymlpublish-release.ymldeclares one block across ten jobs, so most of its jobs are covered by neither their own declaration nor a workflow-level default.Why this is a separate change
These sit on the publish path rather than the validation path, and least privilege there is not simply
contents: read. A Docker build-and-push job needs registry credentials,get-version-taskruns NBGV against history, andpublish-plan-taskreads the trigger context. Getting any of them wrong breaks publishing rather than failing a lint, and the failure would surface on a weekly scheduled run rather than on a pull request.That is the opposite risk profile from the validation fix, which was proven safe by the hub's own copy running the identical actions under
contents: read. So it wants its own change, with each job's required scope established from what its steps actually call, and ideally a dispatch run to prove the publish path still works before it is relied on.Suggested approach
Establish the needed scope per job rather than applying
contents: readbroadly, then add a workflow-level default topublish-release.ymlso a future job cannot silently inherit write. GOVERNANCE.md "Workflow YAML Conventions" states the rule ("Grant least privilege; a callee's extra scope is granted by the caller"), and WORKFLOW.md's D-guarantees describe what each job in the chain is for, which is the input to deciding each scope.