| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- package scan
- import (
- "os"
- "path/filepath"
- "github.com/go-git/go-git/v5"
- "github.com/zricethezav/gitleaks/v7/config"
- "github.com/zricethezav/gitleaks/v7/options"
- )
- // Scanner abstracts unique scanner internals while exposing the Scan function which
- // returns a report.
- type Scanner interface {
- Scan() (Report, error)
- }
- // ScannerType is the scanner type which is determined based on program arguments
- type ScannerType int
- const (
- typeRepoScanner ScannerType = iota + 1
- typeDirScanner
- typeCommitScanner
- typeCommitsScanner
- typeUnstagedScanner
- typeFilesAtCommitScanner
- typeNoGitScanner
- typeEmpty
- )
- // NewScanner accepts options and a config which will be used to determine and create a
- // new scanner which is then returned.
- func NewScanner(opts options.Options, cfg config.Config) (Scanner, error) {
- var (
- repo *git.Repository
- err error
- )
- // We want to return a dir scanner immediately since if the scan type is a directory scan
- // we don't want to clone/open a repo until inside ParentScanner.Scan
- st, err := scanType(opts)
- if err != nil {
- return nil, err
- }
- if st == typeDirScanner {
- if opts.AdditionalConfig != "" {
- additionalCfg, err := config.LoadAdditionalConfig(opts.AdditionalConfig)
- if err != nil {
- return nil, err
- }
- cfg = cfg.AppendConfig(additionalCfg)
- }
- return NewParentScanner(opts, cfg), nil
- }
- // Clone or open a repo if we need it
- if needsRepo(st) {
- repo, err = getRepo(opts)
- if err != nil {
- return nil, err
- }
- }
- // load up alternative config if possible, if not use default/specified config. Will append if AppendRepoConfig is true
- if opts.RepoConfigPath != "" && !opts.NoGit {
- repoCfg, err := config.LoadRepoConfig(repo, opts.RepoConfigPath)
- if err != nil {
- return nil, err
- }
- if opts.AppendRepoConfig {
- cfg = cfg.AppendConfig(repoCfg)
- } else {
- cfg = repoCfg
- }
- }
- // append additional config with the rest of the config
- if opts.AdditionalConfig != "" {
- additionalCfg, err := config.LoadAdditionalConfig(opts.AdditionalConfig)
- if err != nil {
- return nil, err
- }
- cfg = cfg.AppendConfig(additionalCfg)
- }
- switch st {
- case typeCommitScanner:
- c, err := obtainCommit(repo, opts.Commit)
- if err != nil {
- return nil, err
- }
- return NewCommitScanner(opts, cfg, repo, c), nil
- case typeCommitsScanner:
- commits, err := optsToCommits(opts)
- if err != nil {
- return nil, err
- }
- return NewCommitsScanner(opts, cfg, repo, commits), nil
- case typeFilesAtCommitScanner:
- c, err := obtainCommit(repo, opts.FilesAtCommit)
- if err != nil {
- return nil, err
- }
- return NewFilesAtCommitScanner(opts, cfg, repo, c), nil
- case typeUnstagedScanner:
- return NewUnstagedScanner(opts, cfg, repo), nil
- case typeDirScanner:
- return NewParentScanner(opts, cfg), nil
- case typeNoGitScanner:
- return NewNoGitScanner(opts, cfg), nil
- default:
- return NewRepoScanner(opts, cfg, repo), nil
- }
- }
- func scanType(opts options.Options) (ScannerType, error) {
- if opts.Commit != "" {
- return typeCommitScanner, nil
- }
- if opts.Commits != "" || opts.CommitsFile != "" {
- return typeCommitsScanner, nil
- }
- if opts.FilesAtCommit != "" {
- return typeFilesAtCommitScanner, nil
- }
- if opts.Path != "" && !opts.NoGit {
- if opts.CheckUncommitted() {
- return typeUnstagedScanner, nil
- }
- _, err := os.Stat(filepath.Join(opts.Path))
- if err != nil {
- return typeEmpty, err
- }
- // check if path/.git exists, if it does, this is a repo scan
- // if not this is a multi-repo scan
- _, err = os.Stat(filepath.Join(opts.Path, ".git"))
- if os.IsNotExist(err) {
- return typeDirScanner, nil
- }
- return typeRepoScanner, nil
- }
- if opts.Path != "" && opts.NoGit {
- _, err := os.Stat(filepath.Join(opts.Path))
- if err != nil {
- return typeEmpty, err
- }
- return typeNoGitScanner, nil
- }
- if opts.CheckUncommitted() {
- return typeUnstagedScanner, nil
- }
- // default to the most commonly used scanner, RepoScanner
- return typeRepoScanner, nil
- }
- func needsRepo(st ScannerType) bool {
- if !(st == typeDirScanner || st == typeNoGitScanner) {
- return true
- }
- return false
- }
|