nogit.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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. relPath, err := filepath.Rel(ngs.opts.Path, p)
  64. if err != nil {
  65. leak.File = p
  66. } else {
  67. leak.File = relPath
  68. }
  69. leak.Rule = rule.Description
  70. leak.Tags = strings.Join(rule.Tags, ", ")
  71. leak.Log(ngs.opts)
  72. leaks <- leak
  73. }
  74. }
  75. f, err := os.Open(p) // #nosec
  76. if err != nil {
  77. return err
  78. }
  79. scanner := bufio.NewScanner(f)
  80. lineNumber := 0
  81. for scanner.Scan() {
  82. lineNumber++
  83. for _, rule := range ngs.cfg.Rules {
  84. line := scanner.Text()
  85. if rule.AllowList.FileAllowed(filepath.Base(p)) ||
  86. rule.AllowList.PathAllowed(p) {
  87. continue
  88. }
  89. offender := rule.Inspect(line)
  90. if offender == "" {
  91. continue
  92. }
  93. if ngs.cfg.Allowlist.RegexAllowed(line) {
  94. continue
  95. }
  96. if rule.File.String() != "" && !rule.HasFileLeak(filepath.Base(p)) {
  97. continue
  98. }
  99. if rule.Path.String() != "" && !rule.HasFilePathLeak(p) {
  100. continue
  101. }
  102. leak := NewLeak(line, offender, defaultLineNumber)
  103. relPath, err := filepath.Rel(ngs.opts.Path, p)
  104. if err != nil {
  105. leak.File = p
  106. } else {
  107. leak.File = relPath
  108. }
  109. leak.LineNumber = lineNumber
  110. leak.Rule = rule.Description
  111. leak.Tags = strings.Join(rule.Tags, ", ")
  112. leak.Log(ngs.opts)
  113. leaks <- leak
  114. }
  115. }
  116. return f.Close()
  117. })
  118. }
  119. go func() {
  120. if err := g.Wait(); err != nil {
  121. log.Error(err)
  122. }
  123. close(leaks)
  124. }()
  125. for leak := range leaks {
  126. scannerReport.Leaks = append(scannerReport.Leaks, leak)
  127. }
  128. return scannerReport, g.Wait()
  129. }