repo.go 2.5 KB

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