From 036ed13f989025ffa040796049c582643c5a2810 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Fri, 27 Mar 2026 15:50:15 +0100 Subject: [PATCH] gitutil: add opt-in support for host git config Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- util/gitutil/git_cli.go | 36 ++++++++++++++++++++--- util/gitutil/git_cli_test.go | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 util/gitutil/git_cli_test.go diff --git a/util/gitutil/git_cli.go b/util/gitutil/git_cli.go index 966e18ac45b2..3bf0737ca544 100644 --- a/util/gitutil/git_cli.go +++ b/util/gitutil/git_cli.go @@ -27,6 +27,7 @@ type GitCLI struct { sshAuthSock string sshKnownHosts string + hostGitConfig bool } // Option provides a variadic option for configuring the git client. @@ -97,6 +98,15 @@ func WithSSHKnownHosts(sshKnownHosts string) Option { } } +// WithHostGitConfig allows git to read the host system and user git config. +// This is intended for client-side local git inspection. The default remains +// isolated so daemon-side callers do not leak host configuration into git. +func WithHostGitConfig() Option { + return func(b *GitCLI) { + b.hostGitConfig = true + } +} + type StreamFunc func(context.Context) (io.WriteCloser, io.WriteCloser, func()) // WithStreams configures a callback for getting the streams for a command. The @@ -108,7 +118,7 @@ func WithStreams(streams StreamFunc) Option { } } -// New initializes a new git client +// NewGitCLI New initializes a new git client func NewGitCLI(opts ...Option) *GitCLI { c := &GitCLI{} for _, opt := range opts { @@ -191,9 +201,27 @@ func (cli *GitCLI) Run(ctx context.Context, args ...string) (_ []byte, err error "GIT_TERMINAL_PROMPT=0", "GIT_SSH_COMMAND=" + getGitSSHCommand(cli.sshKnownHosts), // "GIT_TRACE=1", - "GIT_CONFIG_NOSYSTEM=1", // Disable reading from system gitconfig. - "HOME=/dev/null", // Disable reading from user gitconfig. - "LC_ALL=C", // Ensure consistent output. + "LC_ALL=C", // Ensure consistent output. + } + if cli.hostGitConfig { + for _, ev := range [...]string{ + "HOME", + "XDG_CONFIG_HOME", + "USERPROFILE", + "HOMEDRIVE", + "HOMEPATH", + "GIT_CONFIG_GLOBAL", + "GIT_CONFIG_SYSTEM", + } { + if v, ok := os.LookupEnv(ev); ok { + cmd.Env = append(cmd.Env, ev+"="+v) + } + } + } else { + cmd.Env = append(cmd.Env, + "GIT_CONFIG_NOSYSTEM=1", // Disable reading from system gitconfig. + "HOME=/dev/null", // Disable reading from user gitconfig. + ) } for _, ev := range proxyEnvVars { if v, ok := os.LookupEnv(ev); ok { diff --git a/util/gitutil/git_cli_test.go b/util/gitutil/git_cli_test.go new file mode 100644 index 000000000000..5732f4ee1794 --- /dev/null +++ b/util/gitutil/git_cli_test.go @@ -0,0 +1,57 @@ +package gitutil + +import ( + "context" + "os/exec" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGitCLIConfigEnv(t *testing.T) { + t.Setenv("HOME", "/tmp/home") + t.Setenv("XDG_CONFIG_HOME", "/tmp/xdg") + t.Setenv("USERPROFILE", `C:\Users\tester`) + t.Setenv("HOMEDRIVE", "C:") + t.Setenv("HOMEPATH", `\Users\tester`) + t.Setenv("GIT_CONFIG_GLOBAL", "/tmp/global-gitconfig") + t.Setenv("GIT_CONFIG_SYSTEM", "/tmp/system-gitconfig") + + t.Run("isolated by default", func(t *testing.T) { + var got []string + cli := NewGitCLI(WithExec(func(ctx context.Context, cmd *exec.Cmd) error { + got = append([]string(nil), cmd.Env...) + return nil + })) + _, err := cli.Run(context.Background(), "status") + require.NoError(t, err) + require.Contains(t, got, "GIT_CONFIG_NOSYSTEM=1") + require.Contains(t, got, "HOME=/dev/null") + require.NotContains(t, got, "HOME=/tmp/home") + require.NotContains(t, got, "XDG_CONFIG_HOME=/tmp/xdg") + require.NotContains(t, got, "GIT_CONFIG_GLOBAL=/tmp/global-gitconfig") + require.NotContains(t, got, "GIT_CONFIG_SYSTEM=/tmp/system-gitconfig") + }) + + t.Run("host git config opt-in", func(t *testing.T) { + var got []string + cli := NewGitCLI( + WithHostGitConfig(), + WithExec(func(ctx context.Context, cmd *exec.Cmd) error { + got = append([]string(nil), cmd.Env...) + return nil + }), + ) + _, err := cli.Run(context.Background(), "status") + require.NoError(t, err) + require.NotContains(t, got, "GIT_CONFIG_NOSYSTEM=1") + require.NotContains(t, got, "HOME=/dev/null") + require.Contains(t, got, "HOME=/tmp/home") + require.Contains(t, got, "XDG_CONFIG_HOME=/tmp/xdg") + require.Contains(t, got, `USERPROFILE=C:\Users\tester`) + require.Contains(t, got, "HOMEDRIVE=C:") + require.Contains(t, got, `HOMEPATH=\Users\tester`) + require.Contains(t, got, "GIT_CONFIG_GLOBAL=/tmp/global-gitconfig") + require.Contains(t, got, "GIT_CONFIG_SYSTEM=/tmp/system-gitconfig") + }) +}