Skip to content

Commit 9906874

Browse files
recreate git config during update to prevent config alteration
1 parent 268c11c commit 9906874

File tree

2 files changed

+173
-12
lines changed

2 files changed

+173
-12
lines changed

get_git.go

+56-12
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
125125
return err
126126
}
127127
if err == nil {
128-
err = g.update(ctx, dst, sshKeyFile, ref, depth)
128+
err = g.update(ctx, dst, sshKeyFile, u, ref, depth)
129129
} else {
130130
err = g.clone(ctx, dst, sshKeyFile, u, ref, depth)
131131
}
@@ -228,21 +228,48 @@ func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.UR
228228
return nil
229229
}
230230

231-
func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile, ref string, depth int) error {
232-
// Determine if we're a branch. If we're NOT a branch, then we just
233-
// switch to master prior to checking out
234-
cmd := exec.CommandContext(ctx, "git", "show-ref", "-q", "--verify", "refs/heads/"+ref)
231+
func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile string, u *url.URL, ref string, depth int) error {
232+
// Remove all variations of .git directories
233+
err := removeCaseInsensitiveGitDirectory(dst)
234+
if err != nil {
235+
return err
236+
}
237+
238+
// Initialize the git repository
239+
cmd := exec.CommandContext(ctx, "git", "init")
235240
cmd.Dir = dst
241+
err = getRunCommand(cmd)
242+
if err != nil {
243+
return err
244+
}
236245

237-
if getRunCommand(cmd) != nil {
238-
// Not a branch, switch to default branch. This will also catch
239-
// non-existent branches, in which case we want to switch to default
240-
// and then checkout the proper branch later.
241-
ref = findDefaultBranch(ctx, dst)
246+
// Add the git remote
247+
cmd = exec.CommandContext(ctx, "git", "remote", "add", "origin", "--", u.String())
248+
cmd.Dir = dst
249+
err = getRunCommand(cmd)
250+
if err != nil {
251+
return err
242252
}
243253

244-
// We have to be on a branch to pull
245-
if err := g.checkout(ctx, dst, ref); err != nil {
254+
// Fetch the remote ref
255+
cmd = exec.CommandContext(ctx, "git", "fetch", "origin", "--", ref)
256+
cmd.Dir = dst
257+
err = getRunCommand(cmd)
258+
if err != nil {
259+
return err
260+
}
261+
262+
// Reset the branch to the fetched ref
263+
cmd = exec.CommandContext(ctx, "git", "reset", "--hard", "FETCH_HEAD")
264+
cmd.Dir = dst
265+
err = getRunCommand(cmd)
266+
if err != nil {
267+
return err
268+
}
269+
270+
// Checkout ref branch
271+
err = g.checkout(ctx, dst, ref)
272+
if err != nil {
246273
return err
247274
}
248275

@@ -377,3 +404,20 @@ func checkGitVersion(ctx context.Context, min string) error {
377404

378405
return nil
379406
}
407+
408+
// removeCaseInsensitiveGitDirectory removes all .git directory variations
409+
func removeCaseInsensitiveGitDirectory(dst string) error {
410+
files, err := os.ReadDir(dst)
411+
if err != nil {
412+
return fmt.Errorf("Failed to read the destination directory %s during git update", dst)
413+
}
414+
for _, f := range files {
415+
if strings.EqualFold(f.Name(), ".git") && f.IsDir() {
416+
err := os.RemoveAll(filepath.Join(dst, f.Name()))
417+
if err != nil {
418+
return fmt.Errorf("Failed to remove the .git directory in the destination directory %s during git update", dst)
419+
}
420+
}
421+
}
422+
return nil
423+
}

get_git_test.go

+117
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,123 @@ func TestGitGetter_BadRemoteUrl(t *testing.T) {
866866
}
867867
}
868868

869+
func TestGitGetter_BadGitConfig(t *testing.T) {
870+
if !testHasGit {
871+
t.Log("git not found, skipping")
872+
t.Skip()
873+
}
874+
875+
ctx := context.Background()
876+
g := new(GitGetter)
877+
dst := tempDir(t)
878+
879+
url, err := url.Parse("https://github.com/hashicorp/go-getter")
880+
if err != nil {
881+
t.Fatal(err)
882+
}
883+
884+
_, err = os.Stat(dst)
885+
if err != nil && !os.IsNotExist(err) {
886+
t.Fatalf(err.Error())
887+
}
888+
if err == nil {
889+
// Update the repository containing the bad git config.
890+
// This should remove the bad git config file and initialize a new one.
891+
err = g.update(ctx, dst, testGitToken, url, "main", 1)
892+
} else {
893+
// Clone a repository with a git config file
894+
err = g.clone(ctx, dst, testGitToken, url, "main", 1)
895+
if err != nil {
896+
t.Fatalf(err.Error())
897+
}
898+
899+
// Edit the git config file to simulate a bad git config
900+
gitConfigPath := filepath.Join(dst, ".git", "config")
901+
err = os.WriteFile(gitConfigPath, []byte("bad config"), 0600)
902+
if err != nil {
903+
t.Fatalf(err.Error())
904+
}
905+
906+
// Update the repository containing the bad git config.
907+
// This should remove the bad git config file and initialize a new one.
908+
err = g.update(ctx, dst, testGitToken, url, "main", 1)
909+
}
910+
if err != nil {
911+
t.Fatalf(err.Error())
912+
}
913+
914+
// Check if the .git/config file contains "bad config"
915+
gitConfigPath := filepath.Join(dst, ".git", "config")
916+
configBytes, err := os.ReadFile(gitConfigPath)
917+
if err != nil {
918+
t.Fatalf(err.Error())
919+
}
920+
if strings.Contains(string(configBytes), "bad config") {
921+
t.Fatalf("The .git/config file contains 'bad config'")
922+
}
923+
}
924+
925+
func TestGitGetter_BadGitDirName(t *testing.T) {
926+
if !testHasGit {
927+
t.Log("git not found, skipping")
928+
t.Skip()
929+
}
930+
931+
ctx := context.Background()
932+
g := new(GitGetter)
933+
dst := tempDir(t)
934+
935+
url, err := url.Parse("https://github.com/hashicorp/go-getter")
936+
if err != nil {
937+
t.Fatal(err)
938+
}
939+
940+
_, err = os.Stat(dst)
941+
if err != nil && !os.IsNotExist(err) {
942+
t.Fatalf(err.Error())
943+
}
944+
if err == nil {
945+
// Remove all variations of .git directories
946+
err = removeCaseInsensitiveGitDirectory(dst)
947+
if err != nil {
948+
t.Fatalf(err.Error())
949+
}
950+
} else {
951+
// Clone a repository with a git directory
952+
err = g.clone(ctx, dst, testGitToken, url, "main", 1)
953+
if err != nil {
954+
t.Fatalf(err.Error())
955+
}
956+
957+
// Rename the .git directory to .GIT
958+
oldPath := filepath.Join(dst, ".git")
959+
newPath := filepath.Join(dst, ".GIT")
960+
err = os.Rename(oldPath, newPath)
961+
if err != nil {
962+
t.Fatalf(err.Error())
963+
}
964+
965+
// Remove all variations of .git directories
966+
err = removeCaseInsensitiveGitDirectory(dst)
967+
if err != nil {
968+
t.Fatalf(err.Error())
969+
}
970+
}
971+
if err != nil {
972+
t.Fatalf(err.Error())
973+
}
974+
975+
// Check if the .GIT directory exists
976+
if _, err := os.Stat(filepath.Join(dst, ".GIT")); !os.IsNotExist(err) {
977+
t.Fatalf(".GIT directory still exists")
978+
}
979+
980+
// Check if the .git directory exists
981+
if _, err := os.Stat(filepath.Join(dst, ".git")); !os.IsNotExist(err) {
982+
t.Fatalf(".git directory still exists")
983+
}
984+
}
985+
869986
// gitRepo is a helper struct which controls a single temp git repo.
870987
type gitRepo struct {
871988
t *testing.T

0 commit comments

Comments
 (0)