The shared release authorizer cannot read a tag in a private repo - #2
Draft
ChelseaKR wants to merge 1 commit into
Draft
The shared release authorizer cannot read a tag in a private repo#2ChelseaKR wants to merge 1 commit into
ChelseaKR wants to merge 1 commit into
Conversation
The authorize job checks out with `persist-credentials: false`, which is
right: this workflow should not leave a credential behind for later steps.
The consequence was unnoticed because every repository that had ever
released through it is public.
`git fetch --force origin refs/tags/<tag>` runs against an origin with no
credentials. A public caller is served anonymously and it works. A private
caller is asked for a username, and with no terminal the job dies:
fatal: could not read Username for 'https://github.com':
No such device or address
Measured across all 17 callers of this workflow: the six repositories that
have published a release through it are public (ctdl-validate 3,
fhir-scorecard 2, outcome-receipts 2, transit-delivery-atlas 2, exitdrill 1,
oscal-validate 1). Both private callers -- self-osint-monitor and
govchat-eval -- have published none. The path has never once run against a
private repository.
`actions/checkout` authenticates its own fetch regardless of
`persist-credentials`, which controls only whether the credential is written
into `.git/config` afterwards. So `fetch-tags: true` brings the tag down
over the authenticated fetch, and the unauthenticated network call goes away
rather than being papered over with a token.
Every assertion after it is unchanged: annotated-tag object, ancestor of
main, allowed_signers present, `git verify-tag`. A tag that is genuinely
absent now fails with a message saying to push it, instead of a git
credential prompt.
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.
Opened as a draft on purpose. This workflow is called by 17 repositories, and while the
change is inert until a caller re-pins, a shared release authorizer is not something to land
without you seeing it.
What is broken
The
authorizejob checks out withpersist-credentials: false— which is correct; thisworkflow should not leave a credential behind for later steps. Then it runs:
git fetch --force origin "refs/tags/${TAG}:refs/tags/${TAG}"against an origin that now has no credentials. A public caller is served anonymously and
it works. A private caller is asked for a username, and with no terminal the job dies:
Why nobody noticed
Measured across all 17 callers. The six repositories that have ever published a release
through this workflow are all public — ctdl-validate (3), fhir-scorecard (2),
outcome-receipts (2), transit-delivery-atlas (2), exitdrill (1), oscal-validate (1). The two
private callers,
self-osint-monitorandgovchat-eval, have published none.This path has never once run against a private repository. It was found by
self-osint-monitordispatchingrelease.ymlfor the first time in its history today, on atag that is annotated, SSH-signed, verifying against its committed
allowed_signers, andsitting on green
main. Everything about that release was correct; the shared primitivecould not read the tag.
The fix
actions/checkoutauthenticates its own fetch regardless ofpersist-credentials— thatinput controls only whether the credential is written into
.git/configafterwards. Sofetch-tags: truebrings the tag down over the authenticated fetch, and the unauthenticatednetwork call is removed rather than papered over with a token in a header.
Every assertion after it is unchanged: annotated-tag object, ancestor of
main,allowed_signerspresent,git verify-tag. A tag that is genuinely missing now fails with amessage telling you to push it, instead of a git credential prompt.
Verified: the workflow parses, and the checkout step reads
{"ref"=>"main", "fetch-depth"=>0, "fetch-tags"=>true, "persist-credentials"=>false}.What it does not do
Consumers pin this workflow by SHA, and
v1.0.0does not move, so merging changes nothingfor the 15 public callers until they re-pin. The two that need it are the private ones. I
have not re-pinned anything.
Prepared with AI assistance; reviewed before submission.