# 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.
