scan.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package scan
  2. import (
  3. "os"
  4. "path/filepath"
  5. "github.com/go-git/go-git/v5"
  6. "github.com/zricethezav/gitleaks/v7/config"
  7. "github.com/zricethezav/gitleaks/v7/options"
  8. )
  9. // Scanner abstracts unique scanner internals while exposing the Scan function which
  10. // returns a report.
  11. type Scanner interface {
  12. Scan() (Report, error)
  13. }
  14. // ScannerType is the scanner type which is determined based on program arguments
  15. type ScannerType int
  16. const (
  17. typeRepoScanner ScannerType = iota + 1
  18. typeDirScanner
  19. typeCommitScanner
  20. typeCommitsScanner
  21. typeUnstagedScanner
  22. typeFilesAtCommitScanner
  23. typeNoGitScanner
  24. typeEmpty
  25. )
  26. // NewScanner accepts options and a config which will be used to determine and create a
  27. // new scanner which is then returned.
  28. func NewScanner(opts options.Options, cfg config.Config) (Scanner, error) {
  29. var (
  30. repo *git.Repository
  31. err error
  32. )
  33. // We want to return a dir scanner immediately since if the scan type is a directory scan
  34. // we don't want to clone/open a repo until inside ParentScanner.Scan
  35. st, err := scanType(opts)
  36. if err != nil {
  37. return nil, err
  38. }
  39. if st == typeDirScanner {
  40. return NewParentScanner(opts, cfg), nil
  41. }
  42. // Clone or open a repo if we need it
  43. if needsRepo(st) {
  44. repo, err = getRepo(opts)
  45. if err != nil {
  46. return nil, err
  47. }
  48. }
  49. // load up alternative config if possible, if not use manager's config
  50. if opts.RepoConfigPath != "" && !opts.NoGit {
  51. cfg, err = config.LoadRepoConfig(repo, opts.RepoConfigPath)
  52. if err != nil {
  53. return nil, err
  54. }
  55. }
  56. switch st {
  57. case typeCommitScanner:
  58. c, err := obtainCommit(repo, opts.Commit)
  59. if err != nil {
  60. return nil, err
  61. }
  62. return NewCommitScanner(opts, cfg, repo, c), nil
  63. case typeCommitsScanner:
  64. commits, err := optsToCommits(opts)
  65. if err != nil {
  66. return nil, err
  67. }
  68. return NewCommitsScanner(opts, cfg, repo, commits), nil
  69. case typeFilesAtCommitScanner:
  70. c, err := obtainCommit(repo, opts.FilesAtCommit)
  71. if err != nil {
  72. return nil, err
  73. }
  74. return NewFilesAtCommitScanner(opts, cfg, repo, c), nil
  75. case typeUnstagedScanner:
  76. return NewUnstagedScanner(opts, cfg, repo), nil
  77. case typeDirScanner:
  78. return NewParentScanner(opts, cfg), nil
  79. case typeNoGitScanner:
  80. return NewNoGitScanner(opts, cfg), nil
  81. default:
  82. return NewRepoScanner(opts, cfg, repo), nil
  83. }
  84. }
  85. func scanType(opts options.Options) (ScannerType, error) {
  86. if opts.Commit != "" {
  87. return typeCommitScanner, nil
  88. }
  89. if opts.Commits != "" || opts.CommitsFile != "" {
  90. return typeCommitsScanner, nil
  91. }
  92. if opts.FilesAtCommit != "" {
  93. return typeFilesAtCommitScanner, nil
  94. }
  95. if opts.Path != "" && !opts.NoGit {
  96. if opts.CheckUncommitted() {
  97. return typeUnstagedScanner, nil
  98. }
  99. _, err := os.Stat(filepath.Join(opts.Path))
  100. if err != nil {
  101. return typeEmpty, err
  102. }
  103. // check if path/.git exists, if it does, this is a repo scan
  104. // if not this is a multi-repo scan
  105. _, err = os.Stat(filepath.Join(opts.Path, ".git"))
  106. if os.IsNotExist(err) {
  107. return typeDirScanner, nil
  108. }
  109. return typeRepoScanner, nil
  110. }
  111. if opts.Path != "" && opts.NoGit {
  112. _, err := os.Stat(filepath.Join(opts.Path))
  113. if err != nil {
  114. return typeEmpty, err
  115. }
  116. return typeNoGitScanner, nil
  117. }
  118. if opts.CheckUncommitted() {
  119. return typeUnstagedScanner, nil
  120. }
  121. // default to the most commonly used scanner, RepoScanner
  122. return typeRepoScanner, nil
  123. }
  124. func needsRepo(st ScannerType) bool {
  125. if !(st == typeDirScanner || st == typeNoGitScanner) {
  126. return true
  127. }
  128. return false
  129. }