git.go 2.5 KB

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