| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- package gitleaks
- import (
- "fmt"
- "net"
- "net/url"
- "os"
- "path/filepath"
- "runtime"
- "strings"
- "time"
- "github.com/jessevdk/go-flags"
- log "github.com/sirupsen/logrus"
- )
- // Options for gitleaks
- type Options struct {
- // remote target options
- Repo string `short:"r" long:"repo" description:"Repo url to audit"`
- GithubUser string `long:"github-user" description:"Github user to audit"`
- GithubOrg string `long:"github-org" description:"Github organization to audit"`
- 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/"`
- GithubPR string `long:"github-pr" description:"Github PR url to audit. This does not clone the repo. GITHUB_TOKEN must be set"`
- GitLabUser string `long:"gitlab-user" description:"GitLab user ID to audit"`
- GitLabOrg string `long:"gitlab-org" description:"GitLab group ID to audit"`
- CommitStop string `long:"commit-stop" description:"sha of commit to stop at"`
- Commit string `long:"commit" description:"sha of commit to audit"`
- Depth int64 `long:"depth" description:"maximum commit depth"`
- // local target option
- RepoPath string `long:"repo-path" description:"Path to repo"`
- OwnerPath string `long:"owner-path" description:"Path to owner directory (repos discovered)"`
- // Process options
- Threads int `long:"threads" description:"Maximum number of threads gitleaks spawns"`
- Disk bool `long:"disk" description:"Clones repo(s) to disk"`
- ConfigPath string `long:"config" description:"path to gitleaks config"`
- SSHKey string `long:"ssh-key" description:"path to ssh key"`
- ExcludeForks bool `long:"exclude-forks" description:"exclude forks for organization/user audits"`
- RepoConfig bool `long:"repo-config" description:"Load config from target repo. Config file must be \".gitleaks.toml\""`
- Branch string `long:"branch" description:"Branch to audit"`
- // TODO: IncludeMessages string `long:"messages" description:"include commit messages in audit"`
- // Output options
- Log string `short:"l" long:"log" description:"log level"`
- Verbose bool `short:"v" long:"verbose" description:"Show verbose output from gitleaks audit"`
- Report string `long:"report" description:"path to write report file. Needs to be csv or json"`
- Redact bool `long:"redact" description:"redact secrets from log messages and report"`
- Version bool `long:"version" description:"version number"`
- SampleConfig bool `long:"sample-config" description:"prints a sample config file"`
- }
- // ParseOpts parses the options
- func ParseOpts() *Options {
- var opts Options
- parser := flags.NewParser(&opts, flags.Default)
- _, err := parser.Parse()
- if err != nil {
- if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type != flags.ErrHelp {
- parser.WriteHelp(os.Stdout)
- }
- os.Exit(0)
- }
- if len(os.Args) == 1 {
- parser.WriteHelp(os.Stdout)
- os.Exit(0)
- }
- if opts.Version {
- fmt.Println(version)
- os.Exit(0)
- }
- if opts.SampleConfig {
- fmt.Println(defaultConfig)
- os.Exit(0)
- }
- opts.setLogs()
- err = opts.guard()
- if err != nil {
- log.Fatal(err)
- }
- return &opts
- }
- // optsGuard prevents invalid options
- func (opts *Options) guard() error {
- if opts.GithubOrg != "" && opts.GithubUser != "" {
- return fmt.Errorf("github user and organization set")
- } else if opts.GithubOrg != "" && opts.OwnerPath != "" {
- return fmt.Errorf("github organization set and local owner path")
- } else if opts.GithubUser != "" && opts.OwnerPath != "" {
- return fmt.Errorf("github user set and local owner path")
- }
- if opts.Threads > runtime.GOMAXPROCS(0) {
- return fmt.Errorf("%d available threads", runtime.GOMAXPROCS(0))
- }
- // do the URL Parse and error checking here, so we can skip it later
- // empty string is OK, it will default to the public github URL.
- if opts.GithubURL != "" && opts.GithubURL != defaultGithubURL {
- if !strings.HasSuffix(opts.GithubURL, "/") {
- opts.GithubURL += "/"
- }
- ghURL, err := url.Parse(opts.GithubURL)
- if err != nil {
- return err
- }
- tcpPort := "443"
- if ghURL.Scheme == "http" {
- tcpPort = "80"
- }
- timeout := time.Duration(1 * time.Second)
- _, err = net.DialTimeout("tcp", ghURL.Host+":"+tcpPort, timeout)
- if err != nil {
- return fmt.Errorf("%s unreachable, error: %s", ghURL.Host, err)
- }
- }
- if opts.Report != "" {
- if !strings.HasSuffix(opts.Report, ".json") && !strings.HasSuffix(opts.Report, ".csv") {
- return fmt.Errorf("Report should be a .json or .csv file")
- }
- dirPath := filepath.Dir(opts.Report)
- if _, err := os.Stat(dirPath); os.IsNotExist(err) {
- return fmt.Errorf("%s does not exist", dirPath)
- }
- }
- return nil
- }
- // setLogLevel sets log level for gitleaks. Default is Warning
- func (opts *Options) setLogs() {
- switch opts.Log {
- case "info":
- log.SetLevel(log.InfoLevel)
- case "debug":
- log.SetLevel(log.DebugLevel)
- case "warn":
- log.SetLevel(log.WarnLevel)
- default:
- log.SetLevel(log.InfoLevel)
- }
- log.SetFormatter(&log.TextFormatter{
- FullTimestamp: true,
- })
- }
|