Repository navigation
Keep clone tokens out of git argv and repository config - #2084
Merged
Merged
Conversation
Every tokened platform git command put the token in the remote URL on its argv. Any host user can read that from /proc/<pid>/cmdline unless /proc hides other users' processes, git wrote it into .git/config for the length of a clone, and git answered a 401 from another authority with it: a remote that redirected its ref advertisement to another host was handed the token. TokenedGitCommand now takes the credential separately. A tokened command names the remote by its URL without userinfo. GIT_CONFIG_COUNT carries two entries scoped to the remote's scheme and authority: an empty credential helper that resets the operator's helpers, then an env-only helper that answers get from CODESPACE_GIT_USERNAME and CODESPACE_GIT_PASSWORD with the shell's builtin printf. A redirect to another authority finds no helper that answers for it, so the command fails instead of sending the token. The -c reset leaves argv: git reads GIT_CONFIG_PARAMETERS after GIT_CONFIG_COUNT, so it would wipe the helper. Auto-gc and auto-maintenance are off so no detached child keeps the environment, and trace2 stays off. git-lfs answers a 401 by erasing the credential and asking the helpers again, with no limit, so a helper that always answers keeps a refused token (expired or revoked) retrying until the command times out, tens of failed logins a second. The helper records an erase as a marker in a per-command owner-only directory the runner makes and removes (ConfigHomeEnvVars), then answers nothing and says the remote refused the credential. Without that directory it never answers. With the token out of the URL, an operator's url.<base>.insteadOf or pushInsteadOf rule matches a tokened remote for the first time, and could move it to SSH under the host's key or to another credential. Two more entries map the remote's URL to itself, the longest prefix any rule can match, so a tokened command reaches its remote as it always did. The workspace clone, soft-ref probe, pin fetch rungs and publish (lfs push, push, readback), the launch-base ls-remote, the branch integrator, the acceptance grader and the pack import all go through it, and BuildAuthenticatedUrl is gone. The integrator's base checkout, apply and reset, and the grader's base checkout and apply, carry the credential too: origin no longer does, and git-lfs asks the helpers for the objects those commands download. Rewriting origin after a clone stays as a belt.
ppXD
force-pushed
the
fix/keep-clone-tokens-out-of-git-argv
branch
from
October 7, 2026 13:47
4989d0d to
649b218
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every tokened platform git command now names its remote by a URL without userinfo and carries the token only in its environment. That covers the workspace clone, soft-ref probe, pin fetch rungs, publish (
lfs push,push, readback), the launch-basels-remote, the branch integrator, the acceptance grader and the pack import.GIT_CONFIG_COUNTscopes two entries to the remote's scheme and authority: an empty helper that resets the operator's helpers, then an env-only helper that answers fromCODESPACE_GIT_USERNAMEandCODESPACE_GIT_PASSWORDwith the shell's builtin printf (backend/src/CodeSpace.Core/Services/Agents/Workspace/TokenedGitCommand.cs)./proc/<pid>/cmdline), no.git/config, no operatorstore/cachehelper, no trace2 target and no authority the remote redirects to ever sees the token.BuildAuthenticatedUrlis removed.The helper stops answering once the remote refuses the token. git-lfs answers a 401 by erasing the credential and asking again, with no limit. A helper that always answers kept an expired or revoked token retrying until the 300 s command timeout, at 15–30 failed logins per second. Now:
erasewrites an empty marker in a per-command owner-only directory (CODESPACE_GIT_STATE). The runner creates, binds and removes it throughSandboxSpec.ConfigHomeEnvVars.getanswers nothing and printsthe remote refused this credential; not offering it again, so the command fails at once with the reason.Operator URL rewrites cannot move a tokened command.
url.<remote>.insteadOfandurl.<remote>.pushInsteadOfmap the remote's URL to itself. A rule naming the whole URL is the longest prefix any rule can match, so a rule likeurl."git@github.com:".insteadOf https://github.com/(switch to SSH) or a rule carrying an operator's own token no longer applies. This matches the behaviour before the change, when no rule could match a URL with a token in it.Commands that download LFS objects through origin also carry the credential, since origin no longer holds it:
apply --3way(backend/src/CodeSpace.Core/Services/Supervisor/SupervisorAcceptanceGrader.cs). Before this, grading at an LFS base failed as "base revision not found".Rewriting origin after a clone stays as a belt. If it fails, the clone is still refused (fail closed), even though origin holds no credential.
Requires git 2.31 or later for
GIT_CONFIG_COUNT(the worker image ships 2.43). An older git ignores it, finds no credential and fails rather than leak the token.Test plan
TokenedGitCommandTests:/bin/sh: answersget, stays silent onstore, aftererasestops answering and says why, and answers nothing without a state directory;user:token) for any username.TokenedGitCredentialHelperFlowTests. Real git and git-lfs, a loopback remote that authenticates reads, LFS and pushes, and the operator's store helpers in a scratch HOME. Passes on git 2.33.0, 2.50.1 and 2.56.0.lfs push) and a clone over LFS each fail in under 20 s, after at most 10 batch requests, with the reason in the message. The positive control, a helper that ignores erase, makes more than 50 requests before it is killed.http.extraHeader.GitWorkspaceIsolationE2ETestsandAgentPublishIsolationE2ETestspass where macOS can run them.clone --config http.extraHeader=…in the provider;