filesatcommit.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. package scan
  2. import (
  3. "path/filepath"
  4. "strings"
  5. "github.com/zricethezav/gitleaks/v7/config"
  6. "github.com/zricethezav/gitleaks/v7/options"
  7. "github.com/go-git/go-git/v5"
  8. "github.com/go-git/go-git/v5/plumbing/object"
  9. )
  10. // FilesAtCommitScanner is a files at commit scanner. This differs from CommitScanner
  11. // as CommitScanner generates patches that are scanned. FilesAtCommitScanner instead looks at
  12. // files available at a commit's worktree and scans the entire content of said files.
  13. // Apologies for the awful struct name...
  14. type FilesAtCommitScanner struct {
  15. opts options.Options
  16. cfg config.Config
  17. repo *git.Repository
  18. commit *object.Commit
  19. repoName string
  20. }
  21. // NewFilesAtCommitScanner creates and returns a files at commit scanner
  22. func NewFilesAtCommitScanner(opts options.Options, cfg config.Config, repo *git.Repository, commit *object.Commit) *FilesAtCommitScanner {
  23. fs := &FilesAtCommitScanner{
  24. opts: opts,
  25. cfg: cfg,
  26. repo: repo,
  27. commit: commit,
  28. repoName: getRepoName(opts),
  29. }
  30. return fs
  31. }
  32. // Scan kicks off a FilesAtCommitScanner Scan
  33. func (fs *FilesAtCommitScanner) Scan() (Report, error) {
  34. var scannerReport Report
  35. if fs.cfg.Allowlist.CommitAllowed(fs.commit.Hash.String()) {
  36. return scannerReport, nil
  37. }
  38. fIter, err := fs.commit.Files()
  39. if err != nil {
  40. return scannerReport, err
  41. }
  42. err = fIter.ForEach(func(f *object.File) error {
  43. bin, err := f.IsBinary()
  44. if bin {
  45. return nil
  46. } else if err != nil {
  47. return err
  48. }
  49. if fs.cfg.Allowlist.FileAllowed(filepath.Base(f.Name)) ||
  50. fs.cfg.Allowlist.PathAllowed(f.Name) {
  51. return nil
  52. }
  53. content, err := f.Contents()
  54. if err != nil {
  55. return err
  56. }
  57. // Check individual file path ONLY rules
  58. for _, rule := range fs.cfg.Rules {
  59. if rule.CommitAllowed(fs.commit.Hash.String()) {
  60. continue
  61. }
  62. if rule.HasFileOrPathLeakOnly(f.Name) {
  63. leak := NewLeak("", "Filename or path offender: "+f.Name, defaultLineNumber).WithCommit(fs.commit)
  64. leak.Repo = fs.repoName
  65. leak.File = f.Name
  66. leak.RepoURL = fs.opts.RepoURL
  67. leak.LeakURL = leak.URL()
  68. leak.Rule = rule.Description
  69. leak.Tags = strings.Join(rule.Tags, ", ")
  70. leak.Log(fs.opts)
  71. scannerReport.Leaks = append(scannerReport.Leaks, leak)
  72. continue
  73. }
  74. }
  75. for i, line := range strings.Split(content, "\n") {
  76. for _, rule := range fs.cfg.Rules {
  77. if rule.AllowList.FileAllowed(filepath.Base(f.Name)) ||
  78. rule.AllowList.PathAllowed(f.Name) ||
  79. rule.AllowList.CommitAllowed(fs.commit.Hash.String()) {
  80. continue
  81. }
  82. offender := rule.Inspect(line)
  83. if offender == "" {
  84. continue
  85. }
  86. if fs.cfg.Allowlist.RegexAllowed(line) {
  87. continue
  88. }
  89. if rule.File.String() != "" && !rule.HasFileLeak(filepath.Base(f.Name)) {
  90. continue
  91. }
  92. if rule.Path.String() != "" && !rule.HasFilePathLeak(f.Name) {
  93. continue
  94. }
  95. leak := NewLeak(line, offender, defaultLineNumber).WithCommit(fs.commit)
  96. leak.File = f.Name
  97. leak.LineNumber = i + 1
  98. leak.RepoURL = fs.opts.RepoURL
  99. leak.Repo = fs.repoName
  100. leak.LeakURL = leak.URL()
  101. leak.Rule = rule.Description
  102. leak.Tags = strings.Join(rule.Tags, ", ")
  103. leak.Log(fs.opts)
  104. scannerReport.Leaks = append(scannerReport.Leaks, leak)
  105. }
  106. }
  107. return nil
  108. })
  109. scannerReport.Commits = 1
  110. return scannerReport, err
  111. }