options.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package options
  2. import (
  3. "fmt"
  4. "github.com/jessevdk/go-flags"
  5. log "github.com/sirupsen/logrus"
  6. "github.com/zricethezav/gitleaks-ng/version"
  7. "gopkg.in/src-d/go-git.v4"
  8. "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
  9. "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
  10. "io/ioutil"
  11. "os"
  12. "os/user"
  13. "strings"
  14. )
  15. const (
  16. Success int = iota + 1
  17. LeaksPresent
  18. ErrorEncountered
  19. )
  20. type Options struct {
  21. Verbose bool `short:"v" long:"verbose" description:"Show verbose output from audit"`
  22. Repo string `short:"r" long:"repo" description:"Target repository"`
  23. Config string `long:"config" description:"config path"`
  24. Disk bool `long:"disk" description:"Clones repo(s) to disk"`
  25. Version bool `long:"version" description:"version number"`
  26. Timeout int `long:"timeout" description:"Timeout (s)"`
  27. Username string `long:"username" description:"Username for git repo"`
  28. Password string `long:"password" description:"Password for git repo"`
  29. AccessToken string `long:"access-token" description:"Access token for git repo"`
  30. Commit string `long:"commit" description:"sha of commit to audit"`
  31. Threads int `long:"threads" description:"Maximum number of threads gitleaks spawns"`
  32. SSH string `long:"ssh-key" description:"path to ssh key used for auth"`
  33. Uncommited bool `long:"uncommitted" description:"run gitleaks on uncommitted code"`
  34. RepoPath string `long:"repo-path" description:"Path to repo"`
  35. OwnerPath string `long:"owner-path" description:"Path to owner directory (repos discovered)"`
  36. Branch string `long:"branch" description:"Branch to audit"`
  37. Report string `long:"report" description:"path to write json leaks file"`
  38. Redact bool `long:"redact" description:"redact secrets from log messages and leaks"`
  39. Debug bool `long:"debug" description:"log debug messages"`
  40. RepoConfig bool `long:"repo-config" description:"Load config from target repo. Config file must be \".gitleaks.toml\" or \"gitleaks.toml\""`
  41. // Hosts
  42. Host string `long:"host" description:"git hosting service like gitlab or github. Supported hosts include: Github, Gitlab"`
  43. Organization string `long:"org" description:"organization to audit"`
  44. User string `long:"user" description:"user to audit"` //work
  45. PullRequest string `long:"pr" description:"pull/merge request url"`
  46. }
  47. // ParseOptions is responsible for parsing options passed in by cli. An Options struct
  48. // is returned if successful. This struct is passed around the program
  49. // and will determine how the program executes. If err, an err message or help message
  50. // will be displayed and the program will exit with code 0.
  51. func ParseOptions() (Options, error) {
  52. var opts Options
  53. parser := flags.NewParser(&opts, flags.Default)
  54. _, err := parser.Parse()
  55. if err != nil {
  56. parser.WriteHelp(os.Stdout)
  57. os.Exit(Success)
  58. }
  59. if opts.Version {
  60. fmt.Printf("%s\n", version.Version)
  61. os.Exit(Success)
  62. }
  63. if opts.Debug {
  64. log.SetLevel(log.DebugLevel)
  65. }
  66. return opts, nil
  67. }
  68. // Guard checks to makes sure there are no invalid options set.
  69. // If invalid sets of options are present, a descriptive error will return
  70. // else nil is returned
  71. func (opts Options) Guard() error {
  72. // 1. only one target option set at a time:
  73. // repo, owner-path, repo-path
  74. return nil
  75. }
  76. // cloneOptions returns a git.cloneOptions pointer. The authentication method
  77. // is determined by what is passed in via command-Line options. If No
  78. // Username/PW or AccessToken is available and the repo target is not using the
  79. // git protocol then the repo must be a available via no auth.
  80. func (opts Options) CloneOptions() (*git.CloneOptions, error) {
  81. progress := ioutil.Discard
  82. if opts.Verbose {
  83. progress = os.Stdout
  84. }
  85. if strings.HasPrefix(opts.Repo, "git") {
  86. // using git protocol so needs ssh auth
  87. auth, err := sshAuth(opts)
  88. if err != nil {
  89. return nil, err
  90. }
  91. return &git.CloneOptions{
  92. URL: opts.Repo,
  93. Auth: auth,
  94. Progress: progress,
  95. }, nil
  96. }
  97. if opts.Password != "" && opts.Username != "" {
  98. // auth using username and password
  99. return &git.CloneOptions{
  100. URL: opts.Repo,
  101. Auth: &http.BasicAuth{
  102. Username: opts.Username,
  103. Password: opts.Password,
  104. },
  105. Progress: progress,
  106. }, nil
  107. }
  108. if opts.AccessToken != "" {
  109. return &git.CloneOptions{
  110. URL: opts.Repo,
  111. Auth: &http.BasicAuth{
  112. Username: "gitleaks_user",
  113. Password: opts.AccessToken,
  114. },
  115. Progress: progress,
  116. }, nil
  117. }
  118. if os.Getenv("GITLEAKS_ACCESS_TOKEN") != "" {
  119. return &git.CloneOptions{
  120. URL: opts.Repo,
  121. Auth: &http.BasicAuth{
  122. Username: "gitleaks_user",
  123. Password: os.Getenv("GITLEAKS_ACCESS_TOKEN"),
  124. },
  125. Progress: progress,
  126. }, nil
  127. }
  128. // No Auth, publicly available
  129. return &git.CloneOptions{
  130. URL: opts.Repo,
  131. Progress: progress,
  132. }, nil
  133. }
  134. // sshAuth tried to generate ssh public keys based on what was passed via cli. If no
  135. // path was passed via cli then this will attempt to retrieve keys from the default
  136. // location for ssh keys, $HOME/.ssh/id_rsa. This function is only called if the
  137. // repo url using the git:// protocol.
  138. func sshAuth(opts Options) (*ssh.PublicKeys, error) {
  139. if opts.SSH != "" {
  140. return ssh.NewPublicKeysFromFile("git", opts.SSH, "")
  141. }
  142. c, err := user.Current()
  143. if err != nil {
  144. return nil, err
  145. }
  146. defaultPath := fmt.Sprintf("%s/.ssh/id_rsa", c.HomeDir)
  147. return ssh.NewPublicKeysFromFile("git", defaultPath, "")
  148. }
  149. // openLocal checks what options are set, if no remote targets are set
  150. // then return true
  151. func (opts Options) OpenLocal() bool {
  152. if opts.Uncommited || opts.RepoPath != "" || opts.Repo == "" {
  153. return true
  154. }
  155. return false
  156. }
  157. // CheckUncommitted returns a boolean that indicates whether or not gitleaks should check unstaged pre-commit changes
  158. // or if gitleaks should check the entire git history
  159. func (opts Options) CheckUncommitted() bool {
  160. // check to make sure no remote shit is set
  161. if opts.Uncommited {
  162. return true
  163. }
  164. if opts == (Options{}) {
  165. return true
  166. }
  167. if opts.Repo != "" {
  168. return false
  169. }
  170. if opts.RepoPath != "" {
  171. return false
  172. }
  173. if opts.OwnerPath != "" {
  174. return false
  175. }
  176. if opts.Host != "" {
  177. return false
  178. }
  179. return true
  180. }
  181. // GetAccessToken accepts options and returns a string which is the access token to a git host.
  182. // Setting this option or environment var is necessary if performing an audit with any of the git hosting providers
  183. // in the host pkg. The access token set by cli options takes precedence over env vars.
  184. func GetAccessToken(opts Options) string {
  185. if opts.AccessToken != "" {
  186. return opts.AccessToken
  187. }
  188. return os.Getenv("GITLEAKS_ACCESS_TOKEN")
  189. }