.github/workflows/deploy-uat-comment.yml:11-13 gates only on "comment is on a PR" and "body contains /deploy-uat". issue_comment fires for any GitHub user, so anyone can comment /deploy-uat on their own PR.
That dispatches deploy-pipeline.yml, where build-uat checks out the PR head and runs run build (lines 209-225). The PR author controls webpack.config.js, so this executes their code in CI. Because the dispatch is workflow_dispatch (a trusted event), GITHUB_TOKEN keeps the declared deployments: write / pull-requests: write rather than being downgraded
to read-only as it would be on pull_request. actions/checkout also leaves that token in
.git/config (no persist-credentials: false anywhere), so the build step can read it.
The passing-Build-check precondition doesn't help: build-pipeline.yml:14 runs on pull_request, so fork PRs get a passing Build normally.
Scope: contents is read-only, so there's no push access. The impact is code execution in CI, reuse of that token for deployments and PR writes, and getting unreviewed code onto uat.signalrange.space.
Fix
Add an author check to the if: at line 11:
if: |
github.event.issue.pull_request &&
contains(github.event.comment.body, '/deploy-uat') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)
```comment.yml:11-13` gates only on "comment is on a PR" and "body contains `/deploy-uat`". `issue_comment` fires for any GitHub user, so anyone can comment `/deploy-uat` on their own PR.
That dispatches `deploy pipeline.yml`, where `build-uat` checks out the PR head and runs `npm run build` (lines 209-225). The PR author controls `webpack.config.js`, so this executes their code in CI. Because the dispatch is `workflow_dispatch` (a trusted event), `GITHUB_TOKEN` keeps the declared `deployments: write` / `pull-requests: write` rather than being downgraded to read-only as it would be on `pull_request`. `actions/checkout` also leaves that token in `.git/config` (no `persist credentials: false` anywhere), so the build step can read it.
The passing-`Build`-check precondition doesn't help: `build-pipeline.yml:14` runs on `pull_request`, so fork PRs get a passing `Build` normally.
Scope: `contents` is read-only, so there's no push access. The impact is code execution in CI, reuse of that token for deployments and PR writes, and getting unreviewed code onto uat.signalrange.space.
## Fix
Add an author check to the `if:` at line 11:
```yaml
if: |
github.event.issue.pull_request &&
contains(github.event.comment.body, '/deploy-uat') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)
.github/workflows/deploy-uat-comment.yml:11-13gates only on "comment is on a PR" and "body contains/deploy-uat".issue_commentfires for any GitHub user, so anyone can comment/deploy-uaton their own PR.That dispatches
deploy-pipeline.yml, wherebuild-uatchecks out the PR head and runsrun build(lines 209-225). The PR author controlswebpack.config.js, so this executes their code in CI. Because the dispatch isworkflow_dispatch(a trusted event),GITHUB_TOKENkeeps the declareddeployments: write/pull-requests: writerather than being downgradedto read-only as it would be on
pull_request.actions/checkoutalso leaves that token in.git/config(nopersist-credentials: falseanywhere), so the build step can read it.The passing-
Build-check precondition doesn't help:build-pipeline.yml:14runs onpull_request, so fork PRs get a passingBuildnormally.Scope:
contentsis read-only, so there's no push access. The impact is code execution in CI, reuse of that token for deployments and PR writes, and getting unreviewed code onto uat.signalrange.space.Fix
Add an author check to the
if:at line 11: