options.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package gitleaks
  2. import (
  3. "fmt"
  4. "net"
  5. "net/url"
  6. "os"
  7. "path/filepath"
  8. "runtime"
  9. "strings"
  10. "time"
  11. "github.com/jessevdk/go-flags"
  12. log "github.com/sirupsen/logrus"
  13. )
  14. // Options for gitleaks
  15. type Options struct {
  16. // remote target options
  17. Repo string `short:"r" long:"repo" description:"Repo url to audit"`
  18. GithubUser string `long:"github-user" description:"Github user to audit"`
  19. GithubOrg string `long:"github-org" description:"Github organization to audit"`
  20. GithubURL string `long:"github-url" default:"https://api.github.com/" description:"GitHub API Base URL, use for GitHub Enterprise. Example: https://github.example.com/api/v3/"`
  21. GithubPR string `long:"github-pr" description:"Github PR url to audit. This does not clone the repo. GITHUB_TOKEN must be set"`
  22. GitLabUser string `long:"gitlab-user" description:"GitLab user ID to audit"`
  23. GitLabOrg string `long:"gitlab-org" description:"GitLab group ID to audit"`
  24. CommitStop string `long:"commit-stop" description:"sha of commit to stop at"`
  25. Commit string `long:"commit" description:"sha of commit to audit"`
  26. Depth int64 `long:"depth" description:"maximum commit depth"`
  27. // local target option
  28. RepoPath string `long:"repo-path" description:"Path to repo"`
  29. OwnerPath string `long:"owner-path" description:"Path to owner directory (repos discovered)"`
  30. // Process options
  31. Threads int `long:"threads" description:"Maximum number of threads gitleaks spawns"`
  32. Disk bool `long:"disk" description:"Clones repo(s) to disk"`
  33. ConfigPath string `long:"config" description:"path to gitleaks config"`
  34. SSHKey string `long:"ssh-key" description:"path to ssh key"`
  35. ExcludeForks bool `long:"exclude-forks" description:"exclude forks for organization/user audits"`
  36. RepoConfig bool `long:"repo-config" description:"Load config from target repo. Config file must be \".gitleaks.toml\""`
  37. Branch string `long:"branch" description:"Branch to audit"`
  38. // TODO: IncludeMessages string `long:"messages" description:"include commit messages in audit"`
  39. // Output options
  40. Log string `short:"l" long:"log" description:"log level"`
  41. Verbose bool `short:"v" long:"verbose" description:"Show verbose output from gitleaks audit"`
  42. Report string `long:"report" description:"path to write report file. Needs to be csv or json"`
  43. Redact bool `long:"redact" description:"redact secrets from log messages and report"`
  44. Version bool `long:"version" description:"version number"`
  45. SampleConfig bool `long:"sample-config" description:"prints a sample config file"`
  46. }
  47. // ParseOpts parses the options
  48. func ParseOpts() *Options {
  49. var opts Options
  50. parser := flags.NewParser(&opts, flags.Default)
  51. _, err := parser.Parse()
  52. if err != nil {
  53. if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type != flags.ErrHelp {
  54. parser.WriteHelp(os.Stdout)
  55. }
  56. os.Exit(0)
  57. }
  58. if len(os.Args) == 1 {
  59. parser.WriteHelp(os.Stdout)
  60. os.Exit(0)
  61. }
  62. if opts.Version {
  63. fmt.Println(version)
  64. os.Exit(0)
  65. }
  66. if opts.SampleConfig {
  67. fmt.Println(defaultConfig)
  68. os.Exit(0)
  69. }
  70. opts.setLogs()
  71. err = opts.guard()
  72. if err != nil {
  73. log.Fatal(err)
  74. }
  75. return &opts
  76. }
  77. // optsGuard prevents invalid options
  78. func (opts *Options) guard() error {
  79. if opts.GithubOrg != "" && opts.GithubUser != "" {
  80. return fmt.Errorf("github user and organization set")
  81. } else if opts.GithubOrg != "" && opts.OwnerPath != "" {
  82. return fmt.Errorf("github organization set and local owner path")
  83. } else if opts.GithubUser != "" && opts.OwnerPath != "" {
  84. return fmt.Errorf("github user set and local owner path")
  85. }
  86. if opts.Threads > runtime.GOMAXPROCS(0) {
  87. return fmt.Errorf("%d available threads", runtime.GOMAXPROCS(0))
  88. }
  89. // do the URL Parse and error checking here, so we can skip it later
  90. // empty string is OK, it will default to the public github URL.
  91. if opts.GithubURL != "" && opts.GithubURL != defaultGithubURL {
  92. if !strings.HasSuffix(opts.GithubURL, "/") {
  93. opts.GithubURL += "/"
  94. }
  95. ghURL, err := url.Parse(opts.GithubURL)
  96. if err != nil {
  97. return err
  98. }
  99. tcpPort := "443"
  100. if ghURL.Scheme == "http" {
  101. tcpPort = "80"
  102. }
  103. timeout := time.Duration(1 * time.Second)
  104. _, err = net.DialTimeout("tcp", ghURL.Host+":"+tcpPort, timeout)
  105. if err != nil {
  106. return fmt.Errorf("%s unreachable, error: %s", ghURL.Host, err)
  107. }
  108. }
  109. if opts.Report != "" {
  110. if !strings.HasSuffix(opts.Report, ".json") && !strings.HasSuffix(opts.Report, ".csv") {
  111. return fmt.Errorf("Report should be a .json or .csv file")
  112. }
  113. dirPath := filepath.Dir(opts.Report)
  114. if _, err := os.Stat(dirPath); os.IsNotExist(err) {
  115. return fmt.Errorf("%s does not exist", dirPath)
  116. }
  117. }
  118. return nil
  119. }
  120. // setLogLevel sets log level for gitleaks. Default is Warning
  121. func (opts *Options) setLogs() {
  122. switch opts.Log {
  123. case "info":
  124. log.SetLevel(log.InfoLevel)
  125. case "debug":
  126. log.SetLevel(log.DebugLevel)
  127. case "warn":
  128. log.SetLevel(log.WarnLevel)
  129. default:
  130. log.SetLevel(log.InfoLevel)
  131. }
  132. log.SetFormatter(&log.TextFormatter{
  133. FullTimestamp: true,
  134. })
  135. }