Skip to content

Redirect direct backend git global config to an isolated per-task file#77

Merged
jasonkeung merged 3 commits into
mainfrom
jason/isolate-direct-backend-home
Jun 1, 2026
Merged

Redirect direct backend git global config to an isolated per-task file#77
jasonkeung merged 3 commits into
mainfrom
jason/isolate-direct-backend-home

Conversation

@jasonkeung

@jasonkeung jasonkeung commented May 28, 2026

Copy link
Copy Markdown
Contributor

Problem

When oz-local is run locally by developers, an insteadOf entry gets written to the user's gitconfig, redirecting ssh urls to https. This is not desirable for developers because we generally prefer ssh auth and we get errors when trying to push to git.

Description

The direct backend runs the oz CLI directly on the host, so git config --global writes from the agent (notably the url.<host>.insteadOf rewrites configured by the warp driver) persisted into the real ~/.gitconfig when running locally (oz-local). Docker and Kubernetes backends are unaffected because their home is ephemeral.

This sets GIT_CONFIG_GLOBAL to a per-task .gitconfig for the direct backend (inside the per-task workspace, or a temporary directory in shared --target-dir mode), applied to the agent invocation and the setup/teardown commands. Those global writes now stay isolated and are cleaned up with the task. The agent git operations during the run still work because they inherit GIT_CONFIG_GLOBAL.

Intentionally out of scope

HOME is left unchanged, so the plaintext credential files the driver writes (~/.git-credentials, ~/.config/gh/hosts.yml) still go to the real home. That is deliberate for this change. Source-side hardening is tracked in warpdotdev/warp#11886.

Testing

  • ran oz-local and did not get a git config entry. ran an agent run that used git with no problems.
  • go vet ./internal/worker
  • go test ./... includes a new regression test that runs git config --global insteadOf and asserts it lands in the per-task config, not the real ~/.gitconfig.

Related

This is the direct-backend piece of the broader Oz git-auth cleanup; the writes themselves originate in the warp driver (app/src/ai/agent_sdk/driver/git_credentials.rs, setup_git_config).

  • warpdotdev/warp-agent-docker#109 — removes the duplicate git-auth setup from the container entrypoint.sh, consolidating to the driver. Complementary, container-only; does not run on the direct/local path, so it does not address this bug.
  • REMOTE-1370 phases 4/5 (warp-server) — migrate token delivery from env vars to the taskGitCredentials query and clean up; orthogonal to where git config is written, so they do not fix the home pollution.
  • Harden Oz git credential setup: avoid persistent --global writes and plaintext credential files warp#11886 — the REMOTE-1370 "git credential helper mode" follow-up; the durable source-side fix that would eliminate persistent --global writes and plaintext credential files across all backends.

Oz

Conversation: https://staging.warp.dev/conversation/370f424c-2dc3-4fe1-b4a6-c3c39dcc70fe

Co-Authored-By: Oz oz-agent@warp.dev

Comment thread internal/worker/direct.go Fixed
Comment thread internal/worker/direct.go Fixed
@jasonkeung jasonkeung force-pushed the jason/isolate-direct-backend-home branch from 9045d9d to 2fc5a48 Compare May 29, 2026 17:04
@jasonkeung jasonkeung changed the title Isolate direct backend task home Redirect direct backend git global config to an isolated per-task file May 29, 2026
Comment thread internal/worker/direct.go Fixed
The direct backend runs the oz CLI directly on the host, so git config
--global writes from the agent (notably the url.<host>.insteadOf rewrites
set up by the warp driver) persisted into the real ~/.gitconfig.

Set GIT_CONFIG_GLOBAL to a per-task .gitconfig (inside the per-task
workspace, or a temp dir in shared --target-dir mode) for the agent
invocation and the setup/teardown commands, so those global writes stay
isolated and are removed with the task. HOME is left unchanged, so the
plaintext credential files the driver writes (~/.git-credentials,
~/.config/gh/hosts.yml) are intentionally untouched. Docker and
Kubernetes backends are unaffected.

Add a regression test that runs git config --global insteadOf and asserts
it lands in the per-task config rather than the real ~/.gitconfig.

Co-Authored-By: Oz <oz-agent@warp.dev>
@jasonkeung jasonkeung force-pushed the jason/isolate-direct-backend-home branch from ea226ea to ac86713 Compare May 29, 2026 17:53
The direct backend joins the server-provided task ID with the workspace
root and uses it as a temp-dir name component. A task ID that is absolute
or contains ".." could escape the intended directory (CodeQL
go/path-injection). Reject non-local task IDs via filepath.IsLocal before
any path is constructed, and add a regression test.

Co-Authored-By: Oz <oz-agent@warp.dev>
@jasonkeung jasonkeung force-pushed the jason/isolate-direct-backend-home branch from ac86713 to 12c79c2 Compare May 29, 2026 18:14
@jasonkeung jasonkeung marked this pull request as ready for review May 29, 2026 18:20
Co-Authored-By: Oz <oz-agent@warp.dev>

@harryalbert harryalbert left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! Just had one question about gating

Comment thread internal/worker/direct.go
// global config keeps writes like `git config --global url.<x>.insteadOf` out of
// the developer's real ~/.gitconfig (and $XDG_CONFIG_HOME/git/config) without
// repointing HOME for every tool the agent runs.
func prepareTaskGitConfig(workspaceDir string, usingTargetDir bool) (string, func(), error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like we're not gating this on the oz-local script — OOC is there a reason to do this across the board instead of limiting to only when debugging (maybe self hosted)?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely use a per-task config file for any run with the direct backend. There may be multiple concurrent runs on the same host, and while we can't fully isolate them, we should do so as much as possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also dont think its as straight forward to differentiate between selfhosted workers (used by real clients) and local, and ideally our oz-local is as similar to real production behavior as possible too

Comment thread internal/worker/direct.go
// global config keeps writes like `git config --global url.<x>.insteadOf` out of
// the developer's real ~/.gitconfig (and $XDG_CONFIG_HOME/git/config) without
// repointing HOME for every tool the agent runs.
func prepareTaskGitConfig(workspaceDir string, usingTargetDir bool) (string, func(), error) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely use a per-task config file for any run with the direct backend. There may be multiple concurrent runs on the same host, and while we can't fully isolate them, we should do so as much as possible.

Comment thread internal/worker/direct.go
return err
}
defer cleanupGitConfig()
gitConfigEnv := []string{fmt.Sprintf("GIT_CONFIG_GLOBAL=%s", gitConfigPath)}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to escape the path, in case the workspace dir is set to something with spaces

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added testing for a workspace path with spaces. Since we are using exec.Cmd.Env, it shouldnt need escaping

@jasonkeung jasonkeung merged commit b0cac0e into main Jun 1, 2026
8 checks passed
@jasonkeung jasonkeung deleted the jason/isolate-direct-backend-home branch June 1, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants