nogit.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. package scan
  2. import (
  3. "bufio"
  4. "context"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. log "github.com/sirupsen/logrus"
  9. "github.com/zricethezav/gitleaks/v7/config"
  10. "github.com/zricethezav/gitleaks/v7/options"
  11. "golang.org/x/sync/errgroup"
  12. )
  13. // NoGitScanner is a scanner that absolutely despises git
  14. type NoGitScanner struct {
  15. opts options.Options
  16. cfg config.Config
  17. }
  18. // NewNoGitScanner creates and returns a nogit scanner. This is used for scanning files and directories
  19. func NewNoGitScanner(opts options.Options, cfg config.Config) *NoGitScanner {
  20. ngs := &NoGitScanner{
  21. opts: opts,
  22. cfg: cfg,
  23. }
  24. // no-git scans should ignore .git folders by default
  25. // issue: https://github.com/zricethezav/gitleaks/issues/474
  26. // ngs.cfg.Allowlist
  27. err := ngs.cfg.Allowlist.IgnoreDotGit()
  28. if err != nil {
  29. log.Error(err)
  30. return nil
  31. }
  32. return ngs
  33. }
  34. // Scan kicks off a NoGitScanner Scan
  35. func (ngs *NoGitScanner) Scan() (Report, error) {
  36. var scannerReport Report
  37. g, _ := errgroup.WithContext(context.Background())
  38. paths := make(chan string, 100)
  39. g.Go(func() error {
  40. defer close(paths)
  41. return filepath.Walk(ngs.opts.Path,
  42. func(path string, fInfo os.FileInfo, err error) error {
  43. if err != nil {
  44. return err
  45. }
  46. if fInfo.Mode().IsRegular() {
  47. paths <- path
  48. }
  49. return nil
  50. })
  51. })
  52. leaks := make(chan Leak, 100)
  53. for path := range paths {
  54. p := path
  55. g.Go(func() error {
  56. if ngs.cfg.Allowlist.FileAllowed(filepath.Base(p)) ||
  57. ngs.cfg.Allowlist.PathAllowed(p) {
  58. return nil
  59. }
  60. for _, rule := range ngs.cfg.Rules {
  61. if rule.HasFileOrPathLeakOnly(p) {
  62. leak := NewLeak("", "Filename or path offender: "+p, defaultLineNumber)
  63. leak.File = p
  64. leak.Rule = rule.Description
  65. leak.Tags = strings.Join(rule.Tags, ", ")
  66. leak.Log(ngs.opts)
  67. leaks <- leak
  68. }
  69. }
  70. f, err := os.Open(p) // #nosec
  71. if err != nil {
  72. return err
  73. }
  74. scanner := bufio.NewScanner(f)
  75. lineNumber := 0
  76. for scanner.Scan() {
  77. lineNumber++
  78. for _, rule := range ngs.cfg.Rules {
  79. line := scanner.Text()
  80. if rule.AllowList.FileAllowed(filepath.Base(p)) ||
  81. rule.AllowList.PathAllowed(p) {
  82. continue
  83. }
  84. offender := rule.Inspect(line)
  85. if offender == "" {
  86. continue
  87. }
  88. if ngs.cfg.Allowlist.RegexAllowed(line) {
  89. continue
  90. }
  91. if rule.File.String() != "" && !rule.HasFileLeak(filepath.Base(p)) {
  92. continue
  93. }
  94. if rule.Path.String() != "" && !rule.HasFilePathLeak(p) {
  95. continue
  96. }
  97. leak := NewLeak(line, offender, defaultLineNumber)
  98. leak.File = p
  99. leak.LineNumber = lineNumber
  100. leak.Rule = rule.Description
  101. leak.Tags = strings.Join(rule.Tags, ", ")
  102. leak.Log(ngs.opts)
  103. leaks <- leak
  104. }
  105. }
  106. return f.Close()
  107. })
  108. }
  109. go func() {
  110. if err := g.Wait(); err != nil {
  111. log.Error(err)
  112. }
  113. close(leaks)
  114. }()
  115. for leak := range leaks {
  116. scannerReport.Leaks = append(scannerReport.Leaks, leak)
  117. }
  118. return scannerReport, g.Wait()
  119. }