commit.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package scan
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "strings"
  6. "github.com/zricethezav/gitleaks/v7/config"
  7. "github.com/zricethezav/gitleaks/v7/options"
  8. "github.com/go-git/go-git/v5"
  9. fdiff "github.com/go-git/go-git/v5/plumbing/format/diff"
  10. "github.com/go-git/go-git/v5/plumbing/object"
  11. )
  12. // CommitScanner is a commit scanner
  13. type CommitScanner struct {
  14. cfg config.Config
  15. opts options.Options
  16. repo *git.Repository
  17. repoName string
  18. commit *object.Commit
  19. }
  20. // NewCommitScanner creates and returns a commit scanner
  21. func NewCommitScanner(opts options.Options, cfg config.Config, repo *git.Repository, commit *object.Commit) *CommitScanner {
  22. cs := &CommitScanner{
  23. cfg: cfg,
  24. opts: opts,
  25. repo: repo,
  26. commit: commit,
  27. repoName: getRepoName(opts),
  28. }
  29. return cs
  30. }
  31. // SetRepoName sets the repo name of the scanner.
  32. func (cs *CommitScanner) SetRepoName(repoName string) {
  33. cs.repoName = repoName
  34. }
  35. // Scan kicks off a CommitScanner Scan
  36. func (cs *CommitScanner) Scan() (Report, error) {
  37. var scannerReport Report
  38. defer func() {
  39. if err := recover(); err != nil {
  40. // sometimes the Patch generation will fail due to a known bug in
  41. // sergi's go-diff: https://github.com/sergi/go-diff/issues/89.
  42. return
  43. }
  44. }()
  45. if cs.cfg.Allowlist.CommitAllowed(cs.commit.Hash.String()) {
  46. return scannerReport, nil
  47. }
  48. if len(cs.commit.ParentHashes) == 0 {
  49. facScanner := NewFilesAtCommitScanner(cs.opts, cs.cfg, cs.repo, cs.commit)
  50. return facScanner.Scan()
  51. }
  52. parent, err := cs.commit.Parent(0)
  53. if err != nil {
  54. return scannerReport, err
  55. }
  56. if parent == nil {
  57. return scannerReport, nil
  58. }
  59. patch, err := parent.Patch(cs.commit)
  60. if err != nil || patch == nil {
  61. return scannerReport, fmt.Errorf("could not generate Patch")
  62. }
  63. patchContent := patch.String()
  64. for _, f := range patch.FilePatches() {
  65. if f.IsBinary() {
  66. continue
  67. }
  68. for _, chunk := range f.Chunks() {
  69. if chunk.Type() == fdiff.Add {
  70. _, to := f.Files()
  71. if cs.cfg.Allowlist.FileAllowed(filepath.Base(to.Path())) ||
  72. cs.cfg.Allowlist.PathAllowed(to.Path()) {
  73. continue
  74. }
  75. // Check individual file path ONLY rules
  76. for _, rule := range cs.cfg.Rules {
  77. if rule.CommitAllowed(cs.commit.Hash.String()) {
  78. continue
  79. }
  80. if rule.HasFileOrPathLeakOnly(to.Path()) {
  81. leak := NewLeak("", "Filename or path offender: "+to.Path(), defaultLineNumber).WithCommit(cs.commit)
  82. leak.Repo = cs.repoName
  83. leak.File = to.Path()
  84. leak.RepoURL = cs.opts.RepoURL
  85. leak.LeakURL = leak.URL()
  86. leak.Rule = rule.Description
  87. leak.Tags = strings.Join(rule.Tags, ", ")
  88. leak.Log(cs.opts)
  89. scannerReport.Leaks = append(scannerReport.Leaks, leak)
  90. continue
  91. }
  92. }
  93. lineLookup := make(map[string]bool)
  94. // Check the actual content
  95. for _, line := range strings.Split(chunk.Content(), "\n") {
  96. for _, rule := range cs.cfg.Rules {
  97. if rule.AllowList.FileAllowed(filepath.Base(to.Path())) ||
  98. rule.AllowList.PathAllowed(to.Path()) ||
  99. rule.AllowList.CommitAllowed(cs.commit.Hash.String()) {
  100. continue
  101. }
  102. offender := rule.Inspect(line)
  103. if offender.IsEmpty() {
  104. continue
  105. }
  106. if cs.cfg.Allowlist.RegexAllowed(line) {
  107. continue
  108. }
  109. if rule.File.String() != "" && !rule.HasFileLeak(filepath.Base(to.Path())) {
  110. continue
  111. }
  112. if rule.Path.String() != "" && !rule.HasFilePathLeak(to.Path()) {
  113. continue
  114. }
  115. leak := NewLeak(line, offender.ToString(), defaultLineNumber).WithCommit(cs.commit).WithEntropy(offender.EntropyLevel)
  116. leak.File = to.Path()
  117. leak.LineNumber = extractLine(patchContent, leak, lineLookup)
  118. leak.RepoURL = cs.opts.RepoURL
  119. leak.Repo = cs.repoName
  120. leak.LeakURL = leak.URL()
  121. leak.Rule = rule.Description
  122. leak.Tags = strings.Join(rule.Tags, ", ")
  123. leak.Log(cs.opts)
  124. scannerReport.Leaks = append(scannerReport.Leaks, leak)
  125. }
  126. }
  127. }
  128. }
  129. }
  130. scannerReport.Commits = 1
  131. return scannerReport, nil
  132. }