git.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package detect
  2. import (
  3. "strings"
  4. "sync"
  5. "time"
  6. "github.com/gitleaks/go-gitdiff/gitdiff"
  7. "github.com/rs/zerolog/log"
  8. "github.com/zricethezav/gitleaks/v8/config"
  9. "github.com/zricethezav/gitleaks/v8/report"
  10. godocutil "golang.org/x/tools/godoc/util"
  11. )
  12. // FromGit accepts a gitdiff.File channel (structure output from `git log -p`) and a configuration
  13. // struct. Files from the gitdiff.File channel are then checked against each rule in the configuration to
  14. // check for secrets. If any secrets are found, they are added to the list of findings.
  15. func FromGit(files <-chan *gitdiff.File, cfg config.Config, outputOptions Options) []report.Finding {
  16. var findings []report.Finding
  17. mu := sync.Mutex{}
  18. wg := sync.WaitGroup{}
  19. concurrentGoroutines := make(chan struct{}, MAXGOROUTINES)
  20. commitMap := make(map[string]bool)
  21. for f := range files {
  22. // keep track of commits for logging
  23. if f.PatchHeader != nil {
  24. commitMap[f.PatchHeader.SHA] = true
  25. }
  26. wg.Add(1)
  27. concurrentGoroutines <- struct{}{}
  28. go func(f *gitdiff.File) {
  29. defer func() {
  30. wg.Done()
  31. <-concurrentGoroutines
  32. }()
  33. if f.IsBinary {
  34. return
  35. }
  36. if f.IsDelete {
  37. return
  38. }
  39. commitSHA := ""
  40. // Check if commit is allowed
  41. if f.PatchHeader != nil {
  42. commitSHA = f.PatchHeader.SHA
  43. if cfg.Allowlist.CommitAllowed(f.PatchHeader.SHA) {
  44. return
  45. }
  46. }
  47. for _, tf := range f.TextFragments {
  48. if f.TextFragments == nil {
  49. // TODO fix this in gitleaks gitdiff fork
  50. // https://github.com/gitleaks/gitleaks/issues/11
  51. continue
  52. }
  53. if !godocutil.IsText([]byte(tf.Raw(gitdiff.OpAdd))) {
  54. continue
  55. }
  56. for _, fi := range DetectFindings(cfg, []byte(tf.Raw(gitdiff.OpAdd)), f.NewName, commitSHA) {
  57. // don't add to start/end lines if finding is from a file only rule
  58. if !strings.HasPrefix(fi.Match, "file detected") {
  59. fi.StartLine += int(tf.NewPosition)
  60. fi.EndLine += int(tf.NewPosition)
  61. }
  62. if f.PatchHeader != nil {
  63. fi.Commit = f.PatchHeader.SHA
  64. fi.Message = f.PatchHeader.Message()
  65. if f.PatchHeader.Author != nil {
  66. fi.Author = f.PatchHeader.Author.Name
  67. fi.Email = f.PatchHeader.Author.Email
  68. }
  69. fi.Date = f.PatchHeader.AuthorDate.UTC().Format(time.RFC3339)
  70. }
  71. if outputOptions.Redact {
  72. fi.Redact()
  73. }
  74. if outputOptions.Verbose {
  75. printFinding(fi)
  76. }
  77. mu.Lock()
  78. findings = append(findings, fi)
  79. mu.Unlock()
  80. }
  81. }
  82. }(f)
  83. }
  84. wg.Wait()
  85. log.Debug().Msgf("%d commits scanned. Note: this number might be smaller than expected due to commits with no additions", len(commitMap))
  86. return findings
  87. }