Skip to content

Write-access requirement is overstated, and invalid sessions fall into the wrong error branch #41

Description

@drogers0

Two separate problems around getUploadToken, both confirmed by testing.

1. References to write access imply more access than is actually required

gh image tells users they need write access, in the error message and in the docs. That is not true.

Verified by uploading to a public repo the account has read-only access to:

permissions: {"admin":false,"maintain":false,"push":false,"triage":false,"pull":true}
$ gh image banner.png --repo <owner>/<repo>
![banner.png](https://github.com/user-attachments/assets/…)

The upload completed end to end — token, policy, S3, finalize. pull access was sufficient. Triage
sits above read, so it is very likely fine too (untested).

This matches how the web UI behaves: any user who can comment on a public repo can drag an image into
a comment. Requiring write access was never GitHub's rule.

Affected:

  • internal/upload/token.gouploadToken not found on repo page — do you have write access to %s/%s?
  • README.md — "uploadToken is only issued to users with write access on the target repository."

Both should stop asserting a write-access requirement.

2. The "Sign in to GitHub" case is not caught and falls into the wrong error handler

When the user_session cookie is invalid or expired, GitHub does not redirect and does not return
an error status. It serves HTTP 200 with a sign-in page that has no uploadToken in it.

Cookie state HTTP uploadToken Page served
none (anonymous) 200 present real repo page (~541 KB)
user_session= (empty) 200 present treated as anonymous
invalid / garbage 200 absent <title>Sign in to GitHub · GitHub</title> (~46 KB)

Reproduce:

curl -s -L -H 'Cookie: user_session=deadbeefdeadbeefdeadbeef' https://github.com/<owner>/<repo> \
  | grep -oE '<title>[^<]*</title>'
# <title>Sign in to GitHub · GitHub</title>

Control flow today: status is 200 so we proceed, the regex finds no token, isSAMLProtected returns
false (correctly — this is not the org SSO interstitial), and we fall through to the write-access
error. So the most common real failure mode, an expired session, is reported as a permissions problem.
Users are sent to check repo access when the actual fix is re-extracting the token.

This has been observed in the wild in a public repo's CI, where a stale GH_SESSION_TOKEN secret
surfaced as do you have write access to …?.

Suggested fix: a third branch alongside isSAMLProtected, keyed on the sign-in interstitial (the
Sign in to GitHub title and absent "currentUser"), returning something actionable — e.g. "your
session token is invalid or expired — re-run gh image extract-token."
Structurally the same shape as
the existing SAML detection: a 200 response that is not the real repo page.


Also worth correcting while in the docs: README.md says visibility "inherits from the target
repository, so a private-repo upload requires authentication to view." An unreferenced upload to a
public repo also 404s for anyone but the uploader until it is referenced in rendered content, which
the current wording implies is not the case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions