Conversation
The release job carries id-token: write, so every step in it can ask GitHub for the OIDC token RubyGems trades for a short-lived publishing key. All three actions in that job were referenced by tag. A tag is a mutable pointer: whoever can move it decides what runs inside the job that can publish this gem, and trusted publishing removes the stored secret without removing that. ruby/setup-ruby@v1 was not even a tag. The repository has no v1 tag; v1 is a branch, so the reference resolved to whatever its head was that morning -- currently a0102e0 (v1.324.0). All three are pinned to commit SHAs with the version in a trailing comment for whoever bumps them. Checkout in the release job also gets persist-credentials: false; the job never pushes, and a token left in .git/config is one more thing a later step could pick up. CI declares permissions: contents: read rather than inheriting the repository default. Last, hardening rather than a fix: the publish check appended everything rake release:status wrote to stdout into GITHUB_OUTPUT. Today that is exactly one line -- verified, and bundler's warnings go to stderr -- so there is no bug here. But that line decides whether credentials are fetched and a gem is pushed, and GITHUB_OUTPUT is parsed as key=value pairs, so the step now takes the one anchored publish= line and fails loudly if there is not exactly one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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 release job carries
id-token: write, so every step in it can ask GitHub for the OIDC token RubyGems trades for a short-lived publishing key. All three actions in that job were referenced by tag. A tag is a mutable pointer: whoever can move it decides what runs inside the job that can publish this gem. Trusted publishing removes the stored secret without removing that.ruby/setup-ruby@v1was not even a tag:It's a branch, so the reference resolved to whatever its head was that morning.
The fix
All three pinned to commit SHAs with the version in a trailing comment for whoever bumps them next:
actions/checkout@11d5960…— v4.3.1ruby/setup-ruby@a0102e0…— v1.324.0 (whatv1pointed at)rubygems/configure-rubygems-credentials@dc5a8d8…— v2.1.0Checkout in the release job also gets
persist-credentials: false; the job never pushes, and a token left in.git/configis one more thing a later step could pick up. CI declarespermissions: contents: readrather than inheriting the repository default.One thing that is hardening, not a bug
The publish check appended everything
rake release:statuswrote to stdout into$GITHUB_OUTPUT. I initially wrote this up as an injection risk and it isn't — I checked, and the task emits exactly one clean line (14 bytes, nothing on stderr); bundler's warnings go to stderr (bundler/ui/shell.rb:warn→tell_err). But that line decides whether credentials are fetched and a gem is pushed, and$GITHUB_OUTPUTis parsed askey=valuepairs, so the step now takes the one anchoredpublish=line and fails loudly if there isn't exactly one. Take it or leave it independently of the pins.Both workflow files validate as YAML and the changed shell step parses under
bash -n.One of a series from a security and API-coverage audit. Branches are independent, each off
main.🤖 Generated with Claude Code