ENTIRE_TOKEN Security and Validation Fixes · Entire
Here are the open comments on PR #1321. They come from two reviewers — toothbrush (human) and Copilot (bot).
toothbrush — doc-comment wording suggestions (3)
All // suggestion edits to comments in cmd/git-remote-entire/main.go:
- L139:
the login server URL from its aud claim. Skips contexts.json and the keyring - L163:
login servers are taken from the cluster_cores.json cache (or a live - L174:
login server's /oauth/token, cached per (repo, action) for this invocation.
These are trivial accept-the-suggestion edits to make the doc comments accurate.
Copilot — substantive findings (3 distinct issues)
env_token.go:57— looseaudURL validation.CoreURLFromEnvTokenaccepts any http(s) URL with a non-empty host, including userinfo/query/fragment. Anaudlikehttps://core.example.com?x=1would flow intorepocredsascoreURL + "/oauth/token", producing an invalid endpoint. Suggests rejecting userinfo/query/fragment and returning a canonicalized scheme/host/path URL.main.go:148— emptyENTIRE_TOKENsilently falls back. WhenENTIRE_TOKEN=""(or whitespace), it's treated as unset and falls back to contexts/keyring. This contradicts the stated design that a mis-set token should fail loudly, never fall back.env_token.go:66–76— credential leak in error messages (6 comments). Error strings useu.String(), which includes userinfo if present. Should useu.Redacted()to avoid leaking passwords to stderr/logs.
The Copilot findings 1 and 3 are related — both about hardening the aud URL parsing. Want me to address these? My suggested plan:
- Accept toothbrush's 3 comment-wording suggestions.
- Harden
CoreURLFromEnvTokento reject userinfo/query/fragment and canonicalize (fixes #1). - Switch error messages to
u.Redacted()(fixes #3). - Make empty/whitespace
ENTIRE_TOKENfail loudly instead of falling back (fixes #2).
Should I go ahead and implement all of these, or do you want to review/scope first?
ENTIRE_TOKEN Explanation
It's a login (or sa-session) JWT — a subject token, not an access token
ENTIRE_TOKEN holds a signed JWT that identifies a principal, used as the subject token in an OAuth token exchange. It is not the token that actually authenticates git operations — it's the credential you trade in to mint those.
What's inside it
tokens.ParseClaims reads these claims — without verifying the signature (that's the issuing server's job; the CLI only reads claims for routing):
| Claim | Use |
|---|---|
iss |
issuing core (may be a regional core) |
sub |
principal subject |
aud |
home-region core URL — what we route the exchange to |
handle |
display name of the principal |
exp/iat/nbf |
lifetime |
Note the deliberate choice: the code keys on aud, not iss because iss may be a regional core that can't mint the cross-region exchange, whereas aud is the home-region URL that STS routing expects.
Why this PR has a security gate around it
Because the exchange host (coreURL) is derived from the token's unverified aud, a forged token could otherwise redirect your credential to an attacker's host. That's why resolveEnvTokenCreds anchors trust to the clone URL's host you typed — fetching /.well-known/entire-cluster.json over TLS and checking the derived core is one the cluster actually advertises (coreTrusted) before ever POSTing the token to it.
TL;DR: ENTIRE_TOKEN is a signed login/service-account identity JWT. The CLI reads its aud to find the home-region core, verifies that core is trusted for the cluster you're cloning, then exchanges the token (RFC 8693) for short-lived, per-repo access tokens that do the real git auth. The env-var path lets CI inject that identity JWT directly.