Skip to content

Commit 2b1394e

Browse files
authored
fix/batches: discard extracted Git metadata before Git initialization (#1370)
1 parent 0adbf30 commit 2b1394e

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

internal/batches/workspace/bind_workspace.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ func (wc *dockerBindWorkspaceCreator) Create(ctx context.Context, repo *graphql.
4343
}
4444

4545
func (*dockerBindWorkspaceCreator) prepareGitRepo(ctx context.Context, w *dockerBindWorkspace) error {
46+
// Windows can normalize archive entry names such as ".git." to ".git".
47+
// Always discard extracted Git metadata before running Git so repository
48+
// contents cannot configure commands that execute on the host.
49+
if err := os.RemoveAll(filepath.Join(w.dir, ".git")); err != nil {
50+
return errors.Wrap(err, "removing extracted git metadata")
51+
}
52+
4653
if _, err := runGitCmd(ctx, w.dir, "init"); err != nil {
4754
return errors.Wrap(err, "git init failed")
4855
}

internal/batches/workspace/bind_workspace_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,47 @@ func TestDockerBindWorkspaceCreator_Create(t *testing.T) {
143143
})
144144
}
145145

146+
func TestPrepareGitRepoRemovesUntrustedGitMetadata(t *testing.T) {
147+
dir := t.TempDir()
148+
dotGit := filepath.Join(dir, ".git")
149+
if err := os.Mkdir(dotGit, 0755); err != nil {
150+
t.Fatal(err)
151+
}
152+
maliciousConfig := "[core]\n\trepositoryformatversion = 0\n\tbare = false\n\tfsmonitor = ./fsmonitor\n"
153+
if err := os.WriteFile(filepath.Join(dotGit, "config"), []byte(maliciousConfig), 0644); err != nil {
154+
t.Fatal(err)
155+
}
156+
if err := os.WriteFile(filepath.Join(dotGit, "attacker-controlled"), nil, 0644); err != nil {
157+
t.Fatal(err)
158+
}
159+
if err := os.WriteFile(filepath.Join(dir, "fsmonitor"), []byte("#!/bin/sh\ntouch fsmonitor-ran\n"), 0755); err != nil {
160+
t.Fatal(err)
161+
}
162+
if err := os.WriteFile(filepath.Join(dir, "tracked.txt"), []byte("tracked\n"), 0644); err != nil {
163+
t.Fatal(err)
164+
}
165+
166+
creator := &dockerBindWorkspaceCreator{}
167+
workspace := &dockerBindWorkspace{dir: dir}
168+
if err := creator.prepareGitRepo(context.Background(), workspace); err != nil {
169+
t.Fatal(err)
170+
}
171+
172+
if _, err := os.Stat(filepath.Join(dir, "fsmonitor-ran")); !os.IsNotExist(err) {
173+
t.Fatalf("fsmonitor command ran on the host: %v", err)
174+
}
175+
if _, err := os.Stat(filepath.Join(dotGit, "attacker-controlled")); !os.IsNotExist(err) {
176+
t.Fatalf("untrusted Git metadata was preserved: %v", err)
177+
}
178+
config, err := os.ReadFile(filepath.Join(dotGit, "config"))
179+
if err != nil {
180+
t.Fatal(err)
181+
}
182+
if strings.Contains(string(config), "fsmonitor") {
183+
t.Fatalf("untrusted Git config was preserved:\n%s", config)
184+
}
185+
}
186+
146187
func TestDockerBindWorkspace_DiffRestoresTrustedGitConfig(t *testing.T) {
147188
archivePath := zipUpFiles(t, t.TempDir(), map[string]string{
148189
"tracked.txt": "before\n",

0 commit comments

Comments
 (0)