From a70937bfe66ded827732e15ad48478b7d353f0f1 Mon Sep 17 00:00:00 2001 From: Bill Rich Date: Fri, 14 Jan 2022 16:07:45 -0800 Subject: [PATCH] Support remote git repos using https (#9) Co-authored-by: Bill Rich --- main.go | 10 +++++++++- pkg/engine/git.go | 24 +++++------------------ pkg/sources/git/git.go | 44 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/main.go b/main.go index 489e831f7..1517531e8 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "github.com/trufflesecurity/trufflehog/pkg/common" + "github.com/trufflesecurity/trufflehog/pkg/sources/git" "log" "os" "runtime" @@ -79,7 +80,14 @@ func main() { switch cmd { case gitScan.FullCommand(): - err := e.ScanGit(ctx, *gitScanURI, *gitScanBranch, "HEAD", filter) + repoPath, remote, err := git.PrepareRepo(*gitScanURI) + if err != nil || repoPath == "" { + logrus.WithError(err).Fatal("error preparing git repo for scanning") + } + if remote { + defer os.RemoveAll(repoPath) + } + err = e.ScanGit(ctx, repoPath, *gitScanBranch, "HEAD", filter) if err != nil { logrus.WithError(err).Fatal("Failed to scan git.") } diff --git a/pkg/engine/git.go b/pkg/engine/git.go index 7f290d1c3..8f9dae739 100644 --- a/pkg/engine/git.go +++ b/pkg/engine/git.go @@ -2,36 +2,22 @@ package engine import ( "context" - "errors" "fmt" - "github.com/trufflesecurity/trufflehog/pkg/common" - "runtime" - "strings" - gogit "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "github.com/sirupsen/logrus" + "github.com/trufflesecurity/trufflehog/pkg/common" "github.com/trufflesecurity/trufflehog/pkg/pb/source_metadatapb" "github.com/trufflesecurity/trufflehog/pkg/pb/sourcespb" "github.com/trufflesecurity/trufflehog/pkg/sources/git" + "runtime" ) -func (e *Engine) ScanGit(ctx context.Context, gitScanURI, gitScanBranch, headRef string, filter *common.Filter) error { - var path string - switch { - case strings.HasPrefix(gitScanURI, "file://"): - path = strings.TrimPrefix(gitScanURI, "file://") - case strings.HasPrefix(gitScanURI, "https://"): - // TODO: clone repo and get path - return errors.New("TODO: clone repo and get path") - default: - logrus.Fatalf("Unsupported Git URI: %s", gitScanURI) - } - - repo, err := gogit.PlainOpenWithOptions(path, &gogit.PlainOpenOptions{DetectDotGit: true}) +func (e *Engine) ScanGit(ctx context.Context, repoPath, gitScanBranch, headRef string, filter *common.Filter) error { + repo, err := gogit.PlainOpenWithOptions(repoPath, &gogit.PlainOpenOptions{DetectDotGit: true}) if err != nil { - return fmt.Errorf("could open repo: %s: %w", path, err) + return fmt.Errorf("could open repo: %s: %w", repoPath, err) } scanOptions := &gogit.LogOptions{ diff --git a/pkg/sources/git/git.go b/pkg/sources/git/git.go index 9d529a6de..5bc32934c 100644 --- a/pkg/sources/git/git.go +++ b/pkg/sources/git/git.go @@ -211,6 +211,9 @@ func CloneRepoUsingToken(token, url, user string) (clonePath string, repo *git.R return } repo, err = git.PlainClone(clonePath, false, cloneOptions) + if err != nil { + return + } safeRepo, err := stripPassword(url) if err != nil { err = errors.New(err) @@ -234,6 +237,9 @@ func CloneRepoUsingUnauthenticated(url string) (clonePath string, repo *git.Repo return } repo, err = git.PlainClone(clonePath, false, cloneOptions) + if err != nil { + return + } safeRepo, err := stripPassword(url) if err != nil { err = errors.New(err) @@ -546,3 +552,41 @@ func TryAdditionalBaseRefs(repo *git.Repository, base string) (*plumbing.Hash, e return nil, fmt.Errorf("no base refs succeeded for base: %q", base) } +func PrepareRepo(uriString string) (string, bool, error) { + var path string + uri, err := url.Parse(uriString) + if err != nil { + return "", false, fmt.Errorf("unable to parse Git URI: %s", err) + } + + remote := false + switch uri.Scheme { + case "file": + path = fmt.Sprintf("%s%s", uri.Host, uri.Path) + case "https": + remotePath := fmt.Sprintf("%s://%s%s", uri.Scheme, uri.Host, uri.Path) + remote = true + switch { + case uri.User != nil: + log.Debugf("Cloning remote Git repo with authentication") + password, ok := uri.User.Password() + if !ok { + return "", remote, fmt.Errorf("password must be included in Git repo URL when username is provided") + } + path, _, err = CloneRepoUsingToken(password, remotePath, uri.User.Username()) + if err != nil { + return path, remote, fmt.Errorf("failed to clone authenticated Git repo (%s): %s", remotePath, err) + } + default: + log.Debugf("Cloning remote Git repo without authentication") + path, _, err = CloneRepoUsingUnauthenticated(remotePath) + if err != nil { + return path, remote, fmt.Errorf("failed to clone unauthenticated Git repo (%s): %s", remotePath, err) + } + } + default: + return "", remote, fmt.Errorf("unsupported Git URI: %s", uriString) + } + log.Debugf("Git repo local path: %s", path) + return path, remote, nil +}