Workflow security fixes for the release pipelines - #4839
Conversation
The debug jobs in the nightly and stable workflows expanded template expressions directly inside the shell script, including one built on github.head_ref, which is attacker-controlled on pull request events. The stable workflow triggers on closed pull requests, so a crafted branch name could inject shell commands into that runner. Passing the values through environment variables keeps the output identical and removes the injection path.
The canary workflow still referenced SiqiLu/dotnet-bump-version, a repository that no longer exists on GitHub, so the next canary push would fail at action download. Worse, a deleted repository name can be re-registered by anyone, and whoever claims it would run their code in this workflow with its token. The nightly workflow already switched to the majora2007 fork, this aligns the canary workflow with it, pinned by commit SHA.
The nightly, canary and stable workflows push images to Docker Hub and ghcr.io and bump versions with a repository PAT, and all of their actions were referenced by floating tags. A tag is a movable pointer, whoever controls an action repository can point it at different code after the fact, which is how the tj-actions/changed-files incident (CVE-2025-30066) leaked CI secrets from thousands of repositories. An exact commit cannot be retargeted. Every pin keeps the version as a trailing comment so it can be checked against the action's releases page. Versions stay exactly where they were, nothing is upgraded.
The docker publish jobs already scoped their own tokens, this extends the same discipline to the rest. Only the issue commenter keeps a write scope, it posts comments on the issues linked to a release. The debug and merge marker jobs get an empty grant since they never touch the API, and the version bump and documentation jobs authenticate with the repository PAT so their workflow token drops to read only.
|
Extra context since I have not raised this through an issue or Discord first, per the template note. Why these changes, in security terms: The stable workflow triggers on closed pull requests against develop, and its debug step expanded an expression built on github.head_ref inside a shell script. A branch name is attacker-controlled on pull request events, so a crafted name could inject shell commands into the runner that holds the publish credentials. Environment variable indirection removes that path with identical output. SiqiLu/dotnet-bump-version is gone from GitHub. Beyond breaking the canary build at action download, a deleted repository name can be re-registered by anyone, and whoever claims it would run their code in that workflow. Pointing at the majora2007 fork restores what the workflow did before the upstream repo vanished. On the pins: a version tag is a movable pointer, whoever controls an action repository can point it at different code after the fact. The tj-actions/changed-files incident (CVE-2025-30066) leaked CI secrets from thousands of repositories exactly this way. An exact commit cannot be retargeted. Each pin keeps the version as a trailing comment so it can be checked against the releases page, and if you ever enable Dependabot for github-actions it understands the format and keeps pins updated. One heads-up: if the org ever restricts allowed actions in the repo settings with tag patterns such as owner/action@v3, those patterns stop matching SHA refs and workflows fail at startup. The fix is owner/action@* in that setting. Nothing to do if no such restriction is configured. Found and fixed by Plumber's analysis, reviewed and submitted by me. |
Changed
Fixed