directory.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package detect
  2. import (
  3. "io"
  4. "os"
  5. "strings"
  6. "github.com/h2non/filetype"
  7. "github.com/rs/zerolog/log"
  8. "github.com/zricethezav/gitleaks/v8/report"
  9. "github.com/zricethezav/gitleaks/v8/sources"
  10. )
  11. func (d *Detector) DetectFiles(paths <-chan sources.ScanTarget) ([]report.Finding, error) {
  12. for pa := range paths {
  13. d.Sema.Go(func() error {
  14. logger := log.With().Str("path", pa.Path).Logger()
  15. logger.Trace().Msg("Scanning path")
  16. f, err := os.Open(pa.Path)
  17. if err != nil {
  18. if os.IsPermission(err) {
  19. logger.Warn().Msg("Skipping file: permission denied")
  20. return nil
  21. }
  22. return err
  23. }
  24. defer func() {
  25. _ = f.Close()
  26. }()
  27. // Get file size
  28. fileInfo, err := f.Stat()
  29. if err != nil {
  30. return err
  31. }
  32. fileSize := fileInfo.Size()
  33. if d.MaxTargetMegaBytes > 0 {
  34. rawLength := fileSize / 1000000
  35. if rawLength > int64(d.MaxTargetMegaBytes) {
  36. logger.Debug().
  37. Int64("size", rawLength).
  38. Msg("Skipping file: exceeds --max-target-megabytes")
  39. return nil
  40. }
  41. }
  42. // Buffer to hold file chunks
  43. buf := make([]byte, chunkSize)
  44. totalLines := 0
  45. for {
  46. n, err := f.Read(buf)
  47. if err != nil && err != io.EOF {
  48. return err
  49. }
  50. if n == 0 {
  51. break
  52. }
  53. // TODO: optimization could be introduced here
  54. mimetype, err := filetype.Match(buf[:n])
  55. if err != nil {
  56. return err
  57. }
  58. if mimetype.MIME.Type == "application" {
  59. return nil // skip binary files
  60. }
  61. // Count the number of newlines in this chunk
  62. linesInChunk := strings.Count(string(buf[:n]), "\n")
  63. totalLines += linesInChunk
  64. fragment := Fragment{
  65. Raw: string(buf[:n]),
  66. FilePath: pa.Path,
  67. }
  68. if pa.Symlink != "" {
  69. fragment.SymlinkFile = pa.Symlink
  70. }
  71. for _, finding := range d.Detect(fragment) {
  72. // need to add 1 since line counting starts at 1
  73. finding.StartLine += (totalLines - linesInChunk) + 1
  74. finding.EndLine += (totalLines - linesInChunk) + 1
  75. d.addFinding(finding)
  76. }
  77. }
  78. return nil
  79. })
  80. }
  81. if err := d.Sema.Wait(); err != nil {
  82. return d.findings, err
  83. }
  84. return d.findings, nil
  85. }