ENTIRE_TOKEN Environment Override for CI

Claude Code·Opus 4.8[1m]·toothbrush·3mo ago·18hr 9min·2 Checkpoints·16 file changes·+919/-72·82.6K tokens

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

3mo ago·13m

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.goEnvTokenVar const + CoreURLFromEnvToken(rawToken). Extracts the home-region core URL from the JWT's URL-shaped aud claim (http/https + host required). Rejects no-URL-aud with a clear ENTIRE_TOKEN must be a login or sa-session JWT… error.
  • cmd/git-remote-entire/main.go — refactored the auth path into resolveCreds(). ENTIRE_TOKEN branch runs before MigrateLegacyLoginContext/ResolveContextForCluster, builds repocreds.New(coreURL, clusterBaseURL, staticProvider, httpClient), and skips both context resolution and the keyring.
  • env_token_test.go — string/array/missing/opaque aud + malformed + alg:none cases.

Notes:

  • Reused tokens.ParseClaims — it already normalises Audience across string and array forms, so no jwtAudiences port was needed. (auth-go v0.4.0.)
  • Reads aud, not iss, per the reference — STS routing keys on the home-region URL in aud.
  • All unit + integration tests pass; mise run lint:go clean. The one lint failure (mise-tasks/release shellcheck) 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 --cached and git 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 --shortstat is 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.
  • Then use AskUserQuestion exactly once with two options, putting the recommended option first and suffixing its label with (Recommended):
    • Wait for results
    • Run in background

Argument handling:

  • Preserve the user's arguments exactly.
  • Do not strip --wait or --background yourself.
  • Do not weaken the adversarial framing or rewrite the user's focus text.
  • The companion script parses --wait and --background, but Claude Code's Bash(..., run_in_background: true) is what actually detaches the run.
  • /codex:adversarial-review uses the same review target selection as /codex:review.
  • It supports working-tree review, branch review, and --base <ref>.
  • It does not support --scope staged or --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 Bash in the background:
  • Do not call BashOutput or wait for completion in this turn.
  • After launching the command, tell the user: "Codex adversarial review started in the background. Check /codex:status for progress."
3mo ago·3m

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_TOKEN can redirect the token exchange to an arbitrary host (cmd/git-remote-entire/main.go:145-154) resolveCreds trusts CoreURLFromEnvToken(envToken) and immediately builds the repo-credential cache from it. That helper derives the core URL from tokens.ParseClaims, which only decodes unverified JWT claims. In practice, a forged or misissued ENTIRE_TOKEN can make git-remote-entire POST the raw workload/login JWT as subject_token to <aud>/oauth/token on 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 unverified aud alone. 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) CoreURLFromEnvToken accepts both http and https audience URLs. The new ENTIRE_TOKEN flow 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 carrying aud=http://... will therefore send the raw ENTIRE_TOKEN over 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 any http:// STS endpoint.

Next steps:

  • Block release until the env-token exchange endpoint is constrained to trusted HTTPS cores.
  • Add coverage for forged/misconfigured ENTIRE_TOKEN values, including arbitrary-host and http:// 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?

3mo ago·21m

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:

  • CoreURLFromEnvToken now 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 aud must 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 (reuses clusterdiscovery, cached, stale-fallback). Forged aud → abort, token never leaves the machine → kills the SSRF finding.
  • Exported clusterdiscovery.ResolveClusterCores; extracted resolveEnvTokenCreds(...) with explicit clusterHost/cacheDir params (no os.Getenv/DefaultCacheDir globals) 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: trusted aud → succeeds; untrusted aud → aborts with "not a trusted core"; discovery 503 → aborts (no fallback to trusting the token's own aud); malformed/http aud → 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.