Skip to content

Commit

Permalink
Recreate git config during update to prevent git config alteration
Browse files Browse the repository at this point in the history
Backport fix from #497 to v2
  • Loading branch information
nywilken committed Jun 28, 2024
1 parent e310de3 commit b6203bd
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 13 deletions.
83 changes: 70 additions & 13 deletions get_git.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (g *GitGetter) Get(ctx context.Context, req *Request) error {
return err
}
if err == nil {
err = g.update(ctx, req.Dst, sshKeyFile, ref, depth)
err = g.update(ctx, req.Dst, sshKeyFile, ref, req.URL(), depth)
} else {
err = g.clone(ctx, sshKeyFile, depth, req)
}
Expand Down Expand Up @@ -193,28 +193,63 @@ func (g *GitGetter) clone(ctx context.Context, sshKeyFile string, depth int, req
return getRunCommand(cmd)
}

func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile, ref string, depth int) error {
// Determine if we're a branch. If we're NOT a branch, then we just
// switch to master prior to checking out
cmd := exec.CommandContext(ctx, "git", "show-ref", "-q", "--verify", "refs/heads/"+ref)
func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile, ref string, u *url.URL, depth int) error {
// Remove all variations of .git directories
err := removeCaseInsensitiveGitDirectory(dst)
if err != nil {
return err

}

// Initialize the git repository
cmd := exec.CommandContext(ctx, "git", "init")
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

// Add the git remote
cmd = exec.CommandContext(ctx, "git", "remote", "add", "origin", "--", u.String())
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

// Fetch the remote ref
cmd = exec.CommandContext(ctx, "git", "fetch", "--tags")
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

// Fetch the remote ref
cmd = exec.CommandContext(ctx, "git", "fetch", "origin", "--", ref)
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

if getRunCommand(cmd) != nil {
// Not a branch, switch to default branch. This will also catch
// non-existent branches, in which case we want to switch to default
// and then checkout the proper branch later.
ref = findDefaultBranch(ctx, dst)
// Reset the branch to the fetched ref
cmd = exec.CommandContext(ctx, "git", "reset", "--hard", "FETCH_HEAD")
cmd.Dir = dst
err = getRunCommand(cmd)
if err != nil {
return err
}

// We have to be on a branch to pull
// Checkout ref branch
if err := g.checkout(ctx, dst, ref); err != nil {
return err
}

if depth > 0 {
cmd = exec.CommandContext(ctx, "git", "pull", "--depth", strconv.Itoa(depth), "--ff-only")
cmd = exec.CommandContext(ctx, "git", "pull", "origin", "--depth", strconv.Itoa(depth), "--ff-only", "--", ref)
} else {
cmd = exec.CommandContext(ctx, "git", "pull", "--ff-only")
cmd = exec.CommandContext(ctx, "git", "pull", "origin", "--ff-only", "--", ref)
}

cmd.Dir = dst
Expand Down Expand Up @@ -326,6 +361,28 @@ func checkGitVersion(ctx context.Context, min string) error {
return nil
}

// removeCaseInsensitiveGitDirectory removes all .git directory variations
func removeCaseInsensitiveGitDirectory(dst string) error {
files, err := os.ReadDir(dst)

Check failure on line 366 in get_git.go

View workflow job for this annotation

GitHub Actions / go-smb-tests (1.21)

undefined: os.ReadDir

Check failure on line 366 in get_git.go

View workflow job for this annotation

GitHub Actions / go-smb-tests (1.20)

undefined: os.ReadDir
if err != nil {
return fmt.Errorf("Failed to read the destination directory %s during git update", dst)

}
for _, f := range files {
if strings.EqualFold(f.Name(), ".git") && f.IsDir() {
err := os.RemoveAll(filepath.Join(dst, f.Name()))
if err != nil {
return fmt.Errorf("Failed to remove the .git directory in the destination directory %s during git update", dst)

}

}

}
return nil

}

func (g *GitGetter) Detect(req *Request) (bool, error) {
src := req.Src
if len(src) == 0 {
Expand Down
149 changes: 149 additions & 0 deletions get_git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ func TestGitGetter_remoteWithoutMaster(t *testing.T) {
}

// Get again should work
req.Dst = testing_helper.TempDir(t)
if err := g.Get(ctx, req); err != nil {
t.Fatalf("err: %s", err)
}
Expand Down Expand Up @@ -841,6 +842,154 @@ func TestGitGetter_subdirectory_traversal(t *testing.T) {
}
}

func TestGitGetter_BadGitConfig(t *testing.T) {
if !testHasGit {
t.Log("git not found, skipping")
t.Skip()

}

ctx := context.Background()
g := new(GitGetter)
dst := testing_helper.TempDir(t)

url, err := url.Parse("https://github.com/hashicorp/go-getter")
if err != nil {
t.Fatal(err)

}

_, err = os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
t.Fatalf(err.Error())
}

if err == nil {
// Update the repository containing the bad git config.
// This should remove the bad git config file and initialize a new one.
err = g.update(ctx, dst, testGitToken, "main", url, 1)

} else {
// Clone a repository with a git config file
req := &Request{
Dst: dst,
u: url,
}
err = g.clone(ctx, testGitToken, 1, req)
if err != nil {
t.Fatalf(err.Error())

}

// Edit the git config file to simulate a bad git config
gitConfigPath := filepath.Join(dst, ".git", "config")
err = os.WriteFile(gitConfigPath, []byte("bad config"), 0600)

Check failure on line 886 in get_git_test.go

View workflow job for this annotation

GitHub Actions / go-smb-tests (1.21)

undefined: os.WriteFile

Check failure on line 886 in get_git_test.go

View workflow job for this annotation

GitHub Actions / go-smb-tests (1.20)

undefined: os.WriteFile
if err != nil {
t.Fatalf(err.Error())

}

// Update the repository containing the bad git config.
// This should remove the bad git config file and initialize a new one.
err = g.update(ctx, dst, testGitToken, "main", url, 1)

}
if err != nil {
t.Fatalf(err.Error())

}

// Check if the .git/config file contains "bad config"
gitConfigPath := filepath.Join(dst, ".git", "config")
configBytes, err := os.ReadFile(gitConfigPath)

Check failure on line 904 in get_git_test.go

View workflow job for this annotation

GitHub Actions / go-smb-tests (1.21)

undefined: os.ReadFile

Check failure on line 904 in get_git_test.go

View workflow job for this annotation

GitHub Actions / go-smb-tests (1.20)

undefined: os.ReadFile
if err != nil {
t.Fatalf(err.Error())

}
if strings.Contains(string(configBytes), "bad config") {
t.Fatalf("The .git/config file contains 'bad config'")

}

}

func TestGitGetter_BadGitDirName(t *testing.T) {
if !testHasGit {
t.Log("git not found, skipping")
t.Skip()

}

ctx := context.Background()
g := new(GitGetter)
dst := testing_helper.TempDir(t)

url, err := url.Parse("https://github.com/hashicorp/go-getter")
if err != nil {
t.Fatal(err)

}

_, err = os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
t.Fatalf(err.Error())
}
if err == nil {
// Remove all variations of .git directories
err = removeCaseInsensitiveGitDirectory(dst)
if err != nil {
t.Fatalf(err.Error())

}

} else {
// Clone a repository with a git directory
req := &Request{
Dst: dst,
u: url,
}
err = g.clone(ctx, testGitToken, 1, req)
if err != nil {
t.Fatalf(err.Error())

}

// Rename the .git directory to .GIT
oldPath := filepath.Join(dst, ".git")
newPath := filepath.Join(dst, ".GIT")
err = os.Rename(oldPath, newPath)
if err != nil {
t.Fatalf(err.Error())

}

// Remove all variations of .git directories
err = removeCaseInsensitiveGitDirectory(dst)
if err != nil {
t.Fatalf(err.Error())

}

}
if err != nil {
t.Fatalf(err.Error())

}

// Check if the .GIT directory exists
if _, err := os.Stat(filepath.Join(dst, ".GIT")); !os.IsNotExist(err) {
t.Fatalf(".GIT directory still exists")

}

// Check if the .git directory exists
if _, err := os.Stat(filepath.Join(dst, ".git")); !os.IsNotExist(err) {
t.Fatalf(".git directory still exists")

}

}

// gitRepo is a helper struct which controls a single temp git repo.
type gitRepo struct {
t *testing.T
Expand Down

0 comments on commit b6203bd

Please sign in to comment.