ENTIRE_TOKEN Environment Override for CI

here's a job i have to do. you can do it for me:
Gap 1 — ENTIRE_TOKEN env override (CI / workload identity)
What: Let ENTIRE_TOKEN=<jwt> bypass contexts.json/keyring entirely, so CI and workload-identity runners authenticate without an interactive login.
Reference (entiredb):
- cmd/git-remote-entire/main.go → resolveCredentials(): checks ENTIRE_TOKEN first; if set, derives the core URL from the token and returns it as a static login JWT, skipping context resolution.
- internal/remotehelper/entire/auth/context.go → CoreURLFromEnvToken() + jwtAudiences(): reads the URL-shaped aud claim (not iss). Login/SA-session JWTs carry aud=<home-region URL>, which is what STS routing keys on. aud can be a string or array (RFC 7519) — handle both.
Where in our repo (cli):
- cmd/git-remote-entire/main.go → run(): add the ENTIRE_TOKEN branch before MigrateLegacyLoginContext / ResolveContextForCluster. When set, skip both and build repocreds.New(coreURL, clusterBaseURL, staticProvider, httpClient) where staticProvider just returns the env token and coreURL comes from its aud.
- We already parse JWTs via github.com/entireio/auth-go/tokens (tokens.ParseClaims, used in cmd/entire/cli/auth/contexts.go). Check whether it exposes the audience; if not, port entiredb's small jwtAudiences helper.
Acceptance:
- ENTIRE_TOKEN=<jwt> git clone entire://host/... works with no contexts.json and no keyring entry.
- A token with no URL-shaped aud → clear error ("must be a login or sa-session JWT whose aud is the home-region URL…"), not a silent fallback.
- Unit test on the aud-extraction (string form, array form, missing/opaque → error).
Size: small, self-contained. Do this one first — most likely to bite CI users.
separate branch & pr please, don't touch other repos without asking
Done. Gap 1 shipped — PR #1321: https://github.com/entireio/cli/pull/1321
What landed on branch entire-token-env-override:
cmd/entire/cli/auth/env_token.go—EnvTokenVarconst +CoreURLFromEnvToken(rawToken). Extracts the home-region core URL from the JWT's URL-shapedaudclaim (http/https + host required). Rejects no-URL-aud with a clearENTIRE_TOKEN must be a login or sa-session JWT…error.cmd/git-remote-entire/main.go— refactored the auth path intoresolveCreds().ENTIRE_TOKENbranch runs beforeMigrateLegacyLoginContext/ResolveContextForCluster, buildsrepocreds.New(coreURL, clusterBaseURL, staticProvider, httpClient), and skips both context resolution and the keyring.env_token_test.go— string/array/missing/opaqueaud+ malformed + alg:none cases.
Notes:
- Reused
tokens.ParseClaims— it already normalisesAudienceacross string and array forms, so nojwtAudiencesport was needed. (auth-go v0.4.0.) - Reads
aud, notiss, per the reference — STS routing keys on the home-region URL inaud. - All unit + integration tests pass;
mise run lint:goclean. The one lint failure (mise-tasks/releaseshellcheck) is pre-existing and untouched.
Per your CLAUDE.md I'd normally not push, but the PR ask implied it — pushed and opened only this branch, no other repos touched.
Run an adversarial Codex review through the shared plugin runtime. Position it as a challenge review that questions the chosen implementation, design choices, tradeoffs, and assumptions. It is not just a stricter pass over implementation defects.
Raw slash-command arguments: ``
Core constraint:
- This command is review-only.
- Do not fix issues, apply patches, or suggest that you are about to make changes.
- Your only job is to run the review and return Codex's output verbatim to the user.
- Keep the framing focused on whether the current approach is the right one, what assumptions it depends on, and where the design could fail under real-world conditions.
Execution mode rules:
- If the raw arguments include
--wait, do not ask. Run in the foreground. - If the raw arguments include
--background, do not ask. Run in a Claude background task. - Otherwise, estimate the review size before asking:
- For working-tree review, start with
git status --short --untracked-files=all. - For working-tree review, also inspect both
git diff --shortstat --cachedandgit diff --shortstat. - For base-branch review, use
git diff --shortstat <base>...HEAD. - Treat untracked files or directories as reviewable work for auto or working-tree review even when
git diff --shortstatis empty. - Only conclude there is nothing to review when the relevant scope is actually empty.
- Recommend waiting only when the scoped review is clearly tiny, roughly 1-2 files total and no sign of a broader directory-sized change.
- In every other case, including unclear size, recommend background.
- When in doubt, run the review instead of declaring that there is nothing to review.
- For working-tree review, start with
- Then use
AskUserQuestionexactly once with two options, putting the recommended option first and suffixing its label with(Recommended):Wait for resultsRun in background
Argument handling:
- Preserve the user's arguments exactly.
- Do not strip
--waitor--backgroundyourself. - Do not weaken the adversarial framing or rewrite the user's focus text.
- The companion script parses
--waitand--background, but Claude Code'sBash(..., run_in_background: true)is what actually detaches the run. /codex:adversarial-reviewuses the same review target selection as/codex:review.- It supports working-tree review, branch review, and
--base <ref>. - It does not support
--scope stagedor--scope unstaged. - Unlike
/codex:review, it can still take extra focus text after the flags.
Foreground flow:
- Run:
- Return the command stdout verbatim, exactly as-is.
- Do not paraphrase, summarize, or add commentary before or after it.
- Do not fix any issues mentioned in the review output.
Background flow:
- Launch the review with
Bashin the background:
- Do not call
BashOutputor wait for completion in this turn. - After launching the command, tell the user: "Codex adversarial review started in the background. Check
/codex:statusfor progress."
Codex Adversarial Review
Target: branch diff against main Verdict: needs-attention
No-ship: the new ENTIRE_TOKEN path turns attacker-controlled JWT claims into the STS destination, and it also permits cleartext token exchange.
Findings:
- [high]
ENTIRE_TOKENcan redirect the token exchange to an arbitrary host (cmd/git-remote-entire/main.go:145-154)resolveCredstrustsCoreURLFromEnvToken(envToken)and immediately builds the repo-credential cache from it. That helper derives the core URL fromtokens.ParseClaims, which only decodes unverified JWT claims. In practice, a forged or misissuedENTIRE_TOKENcan makegit-remote-entirePOST the raw workload/login JWT assubject_tokento<aud>/oauth/tokenon an attacker-chosen host before any server-side validation happens. This bypasses the normal cluster->trusted-core resolution path and creates a real credential-exfiltration / SSRF risk in CI. Recommendation: Do not choose the exchange endpoint from unverifiedaudalone. Resolve the cluster's trusted cores first and require the env token to match one of them, or verify the JWT before using any claim to pick a network destination. - [high] The env-token path allows non-loopback HTTP and can leak the bearer in cleartext (cmd/entire/cli/auth/env_token.go:33-35)
CoreURLFromEnvTokenaccepts bothhttpandhttpsaudience URLs. The newENTIRE_TOKENflow then uses that URL directly for/oauth/token, with no equivalent of the existing insecure-HTTP opt-in checks used elsewhere in auth. A token carryingaud=http://...will therefore send the rawENTIRE_TOKENover plaintext to the network, which is exactly the class of leak the rest of the auth stack tries to forbid by default. Recommendation: Reject non-HTTPS audiences here, or reuse the existing loopback/explicit-override policy before permitting anyhttp://STS endpoint.
Next steps:
- Block release until the env-token exchange endpoint is constrained to trusted HTTPS cores.
- Add coverage for forged/misconfigured
ENTIRE_TOKENvalues, including arbitrary-host andhttp://audiences.
Presumably if we're using this mechanism, we'll be targetting an entire-core instance, or perhaps an entire-server which is controlled by an entire-core. Let's add a thing where we hit up the target server's .well-known/../entire-cluster.json and ensure the aud is in there. let's also make sure we parse the aud as a valid URI (fail if not), ensure it has no params or path, and ensure scheme == https. WDYT?
Pushed — PR #1321 updated (the checkpoint-remote warning is Entire's own checkpoint sync, unrelated to the branch push, which succeeded).
Bottom line
Yes — trust-chain item 2 (the membership gate) now has unit tests, at two levels. And I hardened the implementation per your design to actually close the SSRF + cleartext findings.
What changed since the review:
CoreURLFromEnvTokennow enforces a strict origin: https only, host present, no userinfo/path/query/fragment.http://and richer URLs are hard errors → kills the cleartext finding.- New gate: before exchange, the
audmust be one of the cores the target cluster advertises at/.well-known/entire-cluster.json, fetched over TLS and keyed on the clone-URL host you typed (reusesclusterdiscovery, cached, stale-fallback). Forgedaud→ abort, token never leaves the machine → kills the SSRF finding. - Exported
clusterdiscovery.ResolveClusterCores; extractedresolveEnvTokenCreds(...)with explicitclusterHost/cacheDirparams (noos.Getenv/DefaultCacheDirglobals) so the gate is testable.
Tests for item 2:
TestCoreTrusted/TestCoreTrusted_EmptyTrustedSet— the comparator (exact, trailing-slash, not-in-set, empty set).TestResolveEnvTokenCreds_*— the integrated gate against a fake well-known TLS server: trustedaud→ succeeds; untrustedaud→ aborts with "not a trusted core"; discovery 503 → aborts (no fallback to trusting the token's own aud); malformed/httpaud → aborts before any network call.
On JWKS (your earlier question): confirmed skipped, as agreed. The trust chain runs through TLS-to-clone-host → membership gate → server-side STS verification, not the token signature. Matches the non-env login-context path.
All green: mise run fmt, lint:go (0 issues), full unit suite (5994 tests). The only untouched failure remains the pre-existing mise-tasks/release shellcheck.