repo.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package scan
  2. import (
  3. "context"
  4. "sync"
  5. "golang.org/x/sync/errgroup"
  6. "github.com/zricethezav/gitleaks/v7/config"
  7. "github.com/zricethezav/gitleaks/v7/options"
  8. "github.com/go-git/go-git/v5"
  9. "github.com/go-git/go-git/v5/plumbing/object"
  10. "github.com/go-git/go-git/v5/plumbing/storer"
  11. log "github.com/sirupsen/logrus"
  12. )
  13. // RepoScanner is a repo scanner
  14. type RepoScanner struct {
  15. opts options.Options
  16. cfg config.Config
  17. repo *git.Repository
  18. throttle *Throttle
  19. repoName string
  20. mtx *sync.Mutex
  21. }
  22. // NewRepoScanner returns a new repo scanner (go figure). This function also
  23. // sets up the leak listener for multi-threaded awesomeness.
  24. func NewRepoScanner(opts options.Options, cfg config.Config, repo *git.Repository) *RepoScanner {
  25. rs := &RepoScanner{
  26. opts: opts,
  27. cfg: cfg,
  28. repo: repo,
  29. throttle: NewThrottle(opts),
  30. repoName: getRepoName(opts),
  31. mtx: &sync.Mutex{},
  32. }
  33. return rs
  34. }
  35. // Scan kicks of a repo scan
  36. func (rs *RepoScanner) Scan() (Report, error) {
  37. var (
  38. scannerReport Report
  39. commits chan *object.Commit
  40. )
  41. logOpts, err := logOptions(rs.repo, rs.opts)
  42. if err != nil {
  43. return scannerReport, err
  44. }
  45. cIter, err := rs.repo.Log(logOpts)
  46. if err != nil {
  47. return scannerReport, err
  48. }
  49. g, _ := errgroup.WithContext(context.Background())
  50. commits = make(chan *object.Commit)
  51. commitNum := 0
  52. g.Go(func() error {
  53. defer close(commits)
  54. err = cIter.ForEach(func(c *object.Commit) error {
  55. if c == nil || depthReached(commitNum, rs.opts) {
  56. return storer.ErrStop
  57. }
  58. if rs.cfg.Allowlist.CommitAllowed(c.Hash.String()) {
  59. return nil
  60. }
  61. commitNum++
  62. commits <- c
  63. if c.Hash.String() == rs.opts.CommitTo {
  64. return storer.ErrStop
  65. }
  66. return err
  67. })
  68. cIter.Close()
  69. return nil
  70. })
  71. for commit := range commits {
  72. c := commit
  73. rs.throttle.Limit()
  74. g.Go(func() error {
  75. commitScanner := NewCommitScanner(rs.opts, rs.cfg, rs.repo, c)
  76. commitScanner.SetRepoName(rs.repoName)
  77. report, err := commitScanner.Scan()
  78. rs.throttle.Release()
  79. if err != nil {
  80. log.Error(err)
  81. }
  82. for _, leak := range report.Leaks {
  83. rs.mtx.Lock()
  84. scannerReport.Leaks = append(scannerReport.Leaks, leak)
  85. rs.mtx.Unlock()
  86. }
  87. return nil
  88. })
  89. }
  90. if err := g.Wait(); err != nil {
  91. log.Error(err)
  92. }
  93. scannerReport.Commits = commitNum
  94. return scannerReport, nil
  95. }
  96. // SetRepoName sets the repo name
  97. func (rs *RepoScanner) SetRepoName(repoName string) {
  98. rs.repoName = repoName
  99. }