Skip to content

The shared release authorizer cannot read a tag in a private repo - #2

Draft
ChelseaKR wants to merge 1 commit into
mainfrom
fix/authorize-tag-fetch-on-private-callers
Draft

The shared release authorizer cannot read a tag in a private repo#2
ChelseaKR wants to merge 1 commit into
mainfrom
fix/authorize-tag-fetch-on-private-callers

Conversation

@ChelseaKR

Copy link
Copy Markdown
Owner

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 authorize job checks out with persist-credentials: false — which is correct; this
workflow 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:

fatal: could not read Username for 'https://github.com': No such device or address

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-monitor and govchat-eval, have published none.

This path has never once run against a private repository. It was found by
self-osint-monitor dispatching release.yml for the first time in its history today, on a
tag that is annotated, SSH-signed, verifying against its committed allowed_signers, and
sitting on green main. Everything about that release was correct; the shared primitive
could not read the tag.

The fix

actions/checkout authenticates its own fetch regardless of persist-credentials — that
input 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 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_signers present, git verify-tag. A tag that is genuinely missing now fails with a
message 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.0 does not move, so merging changes nothing
for 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.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant