Summary
When update-project (or another project safe-output) is configured with its own github-token, gh aw compile sets the entire Process Safe Outputs github-script step's with.github-token to that project token — even if safe-outputs.github-token is explicitly set to a different, least-privilege token for issue/PR handlers.
This ends up in not just possible wrong privileges, but the wrong user/bot doing the changes.
Expected
safe-outputs.github-token → default client for Process Safe Outputs / issue handlers
update-project.github-token → only for Projects v2 (update_project.cjs)
Actual
Compiler prefers the project token for the step client whenever any project op is configured:
// If project operations are configured, prefer the project token for the github-script client.
if projectToken != "" {
configToken = projectToken
} else if data.SafeOutputs != nil && data.SafeOutputs.GitHubToken != "" {
configToken = data.SafeOutputs.GitHubToken
}
(pkg/workflow/compiler_safe_outputs_steps.go)
Rationale in-tree is that Projects v2 cannot use GITHUB_TOKEN, but that does not require elevating the shared step client when handlers can use per-config tokens.
Minimal repro
---
on: workflow_dispatch
permissions:
contents: read
issues: read
safe-outputs:
github-token: ${{ secrets.ISSUE_TOKEN }} # least-privilege: issues:write
add-comment:
max: 1
update-project:
project: "https://github.com/orgs/example/projects/1"
max: 1
github-token: ${{ secrets.PROJECT_TOKEN }} # classic PAT: project
---
# noop
Observed lock output
Process Safe Outputs step gets the project token:
- name: Process Safe Outputs
uses: actions/github-script@…
with:
github-token: ${{ secrets.PROJECT_TOKEN }} # unexpected: ignored ISSUE_TOKEN
Not:
github-token: ${{ secrets.ISSUE_TOKEN }}
Impact
Any workflow that splits credentials (ephemeral/App token for issues + classic PAT for Projects) cannot keep the PAT off issue writes at the step-client level. Workarounds: per-handler github-token on every non-project handler (those that call createAuthenticatedGitHubClient), or omit update-project from the same safe-outputs job.
Suggested fix
Keep safe-outputs.github-token as the Process Safe Outputs step client. Pass the project token only via GH_AW_PROJECT_GITHUB_TOKEN / handler config, and have project handlers always use that dedicated client (not global.github from the elevated step token).
Summary
When
update-project(or another project safe-output) is configured with its owngithub-token,gh aw compilesets the entire Process Safe Outputsgithub-scriptstep'swith.github-tokento that project token — even ifsafe-outputs.github-tokenis explicitly set to a different, least-privilege token for issue/PR handlers.This ends up in not just possible wrong privileges, but the wrong user/bot doing the changes.
Expected
safe-outputs.github-token→ default client for Process Safe Outputs / issue handlersupdate-project.github-token→ only for Projects v2 (update_project.cjs)Actual
Compiler prefers the project token for the step client whenever any project op is configured:
(
pkg/workflow/compiler_safe_outputs_steps.go)Rationale in-tree is that Projects v2 cannot use
GITHUB_TOKEN, but that does not require elevating the shared step client when handlers can use per-config tokens.Minimal repro
Observed lock output
Process Safe Outputs step gets the project token:
Not:
Impact
Any workflow that splits credentials (ephemeral/App token for issues + classic PAT for Projects) cannot keep the PAT off issue writes at the step-client level. Workarounds: per-handler
github-tokenon every non-project handler (those that callcreateAuthenticatedGitHubClient), or omitupdate-projectfrom the same safe-outputs job.Suggested fix
Keep
safe-outputs.github-tokenas the Process Safe Outputs step client. Pass the project token only viaGH_AW_PROJECT_GITHUB_TOKEN/ handler config, and have project handlers always use that dedicated client (notglobal.githubfrom the elevated step token).