git-remote-entire: trim surrounding whitespace from ENTIRE_TOKEN · Entire
git-remote-entire: trim surrounding whitespace from ENTIRE_TOKEN
316e109·
Soph·1mo ago·4 files·+42 added/-6 removed
A token sourced via $(cat token) or a here-doc commonly carries a trailing newline; previously that padded-but-valid token failed at JWT parse (and the newline would also be POSTed verbatim to /oauth/token). Trim the value inside the env-token path so both aud-derivation and the exchanged subject_token use the cleaned token.
Whitespace-only is unchanged: it still enters the env path (raw != "" at the caller) and fails closed with "ENTIRE_TOKEN is set but blank" — it never falls back to context auth. Only a truly empty/unset ENTIRE_TOKEN is treated as unset.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Changes
4
cmd
entire/cli/auth
Menv_token.go+7/-6
Menv_token_test.go+10
git-remote-entire
Mmain.go+5
Mmain_test.go+20
40 unmodified lines
41
42
43
44
45
46
47
48
49
44
45
46
47
48
49
50
51
52
53
40 unmodified lines
// validated strictly. A token with no URL-shaped aud is rejected with a clear
// error rather than silently falling back to context resolution.
func CoreURLFromEnvToken(rawToken string) (string, error) {
// A blank-but-present value (e.g. ENTIRE_TOKEN=" ") fails closed rather
// than silently falling back to context auth — but give it a clearer
// message than the raw JWT-parse error. Note: only whitespace-only values
// reach here; a truly empty ENTIRE_TOKEN is treated as unset by the caller
// and never enters the env-token path.
if strings.TrimSpace(rawToken) == "" {
// Trim surrounding whitespace so a token sourced via $(cat token) or a
// here-doc — which commonly carries a trailing newline — still parses. A
// value that is *only* whitespace trims to empty and fails closed with a
// clear message; it never falls back to context auth (a truly empty
// ENTIRE_TOKEN is treated as unset by the caller and never reaches here).
rawToken = strings.TrimSpace(rawToken)
if rawToken == "" {
return "", fmt.Errorf("%s is set but blank", EnvTokenVar)
}
claims, err := tokens.ParseClaims(rawToken)
Mcmd/entire/cli/auth/env_token.go+7/-6
122 unmodified lines
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
122 unmodified lines
}
}
func TestCoreURLFromEnvToken_TrimsSurroundingWhitespace(t *testing.T) {
t.Parallel()
// A valid token padded with leading/trailing whitespace (e.g. a trailing
// newline from $(cat token)) must trim and parse, not fail.
token := makeJWT(t, `{\"sub\":\"ci-runner\",\"aud\":\"https://core.us.entire.io\"}`)
got, err := CoreURLFromEnvToken(" " + token + "\n")
require.NoError(t, err)
assert.Equal(t, "https://core.us.entire.io", got)
}
func TestCoreURLFromEnvToken_RejectsAlgNone(t *testing.T) {
t.Parallel()
// alg:none with a URL-shaped aud must still be rejected at the parse layer.
Mcmd/entire/cli/auth/env_token_test.go+10
181 unmodified lines
182
183
184
185
186
187
188
189
190
191
192
181 unmodified lines
// its /.well-known/entire-cluster.json), not to the token's own claims. Without
// this gate a forged aud could redirect the token to an attacker-chosen host.
func resolveEnvTokenCreds(ctx context.Context, envToken, clusterHost, clusterBaseURL, cacheDir string, httpClient *http.Client) (*repocreds.Cache, error) {
// Trim once here so both the aud-derivation and the exchanged subject_token
// use the cleaned value. A token sourced via $(cat token) often carries a
// trailing newline that would otherwise be POSTed verbatim to /oauth/token
// and rejected. Whitespace-only still fails closed inside CoreURLFromEnvToken.
envToken = strings.TrimSpace(envToken)
coreURL, err := auth.CoreURLFromEnvToken(envToken)
if err != nil {
return nil, err //nolint:wrapcheck // CoreURLFromEnvToken already returns a user-facing, ENTIRE_TOKEN-prefixed error
Mcmd/git-remote-entire/main.go+5
120 unmodified lines
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
120 unmodified lines
}
}
func TestResolveEnvTokenCreds_TrimsSurroundingWhitespace(t *testing.T) {
t.Parallel()
// A trusted token padded with whitespace (trailing newline from
// $(cat token)) must succeed: the trim applies to both aud-derivation and
// the subject_token used for exchange.
const core = "https://core.us.entire.io"
srv, clusterHost := wellKnownServer(t, []string{core})
creds, err := resolveEnvTokenCreds(
t.Context(), " "+makeTestJWT(t, core)+"\n", clusterHost,
"https://cluster.example.com", t.TempDir(), srv.Client(),
)
if err != nil {
t.Fatalf("expected padded-but-valid token to succeed, got: %v", err)
}
if creds == nil {
t.Fatal("expected non-nil creds for padded trusted token")
}
}
func TestResolveEnvTokenCreds_UntrustedAudAborts(t *testing.T) {
t.Parallel()
// The cluster advertises only core.us; the token's aud points elsewhere.