Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions util/gitutil/git_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type GitCLI struct {

sshAuthSock string
sshKnownHosts string
hostGitConfig bool
}

// Option provides a variadic option for configuring the git client.
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
57 changes: 57 additions & 0 deletions util/gitutil/git_cli_test.go
Original file line number Diff line number Diff line change
@@ -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")
})
}
Loading