directory.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  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 f.Close()
  25. // Get file size
  26. fileInfo, err := f.Stat()
  27. if err != nil {
  28. return err
  29. }
  30. fileSize := fileInfo.Size()
  31. if d.MaxTargetMegaBytes > 0 {
  32. rawLength := fileSize / 1000000
  33. if rawLength > int64(d.MaxTargetMegaBytes) {
  34. logger.Debug().
  35. Int64("size", rawLength).
  36. Msgf("Skipping file: exceeds --max-target-megabytes")
  37. return nil
  38. }
  39. }
  40. // Buffer to hold file chunks
  41. buf := make([]byte, chunkSize)
  42. totalLines := 0
  43. for {
  44. n, err := f.Read(buf)
  45. if err != nil && err != io.EOF {
  46. return err
  47. }
  48. if n == 0 {
  49. break
  50. }
  51. // TODO: optimization could be introduced here
  52. mimetype, err := filetype.Match(buf[:n])
  53. if err != nil {
  54. return err
  55. }
  56. if mimetype.MIME.Type == "application" {
  57. return nil // skip binary files
  58. }
  59. // Count the number of newlines in this chunk
  60. linesInChunk := strings.Count(string(buf[:n]), "\n")
  61. totalLines += linesInChunk
  62. fragment := Fragment{
  63. Raw: string(buf[:n]),
  64. FilePath: pa.Path,
  65. }
  66. if pa.Symlink != "" {
  67. fragment.SymlinkFile = pa.Symlink
  68. }
  69. for _, finding := range d.Detect(fragment) {
  70. // need to add 1 since line counting starts at 1
  71. finding.StartLine += (totalLines - linesInChunk) + 1
  72. finding.EndLine += (totalLines - linesInChunk) + 1
  73. d.addFinding(finding)
  74. }
  75. }
  76. return nil
  77. })
  78. }
  79. if err := d.Sema.Wait(); err != nil {
  80. return d.findings, err
  81. }
  82. return d.findings, nil
  83. }