1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/zricethezav/gitleaks/v8/config"
)
const banner = `
○
│╲
│ ○
○ ░
░ gitleaks
`
const configDescription = `config file path
order of precedence:
1. --config/-c
2. env var GITLEAKS_CONFIG
3. (--source/-s)/.gitleaks.toml
If none of the three options are used, then gitleaks will use the default config`
var rootCmd = &cobra.Command{
Use: "gitleaks",
Short: "Gitleaks scans code, past or present, for secrets",
}
func init() {
cobra.OnInitialize(initLog)
rootCmd.PersistentFlags().StringP("config", "c", "", configDescription)
rootCmd.PersistentFlags().Int("exit-code", 1, "exit code when leaks have been encountered")
rootCmd.PersistentFlags().StringP("source", "s", ".", "path to source (default: $PWD)")
rootCmd.PersistentFlags().StringP("report-path", "r", "", "report file")
rootCmd.PersistentFlags().StringP("report-format", "f", "json", "output format (json, csv, sarif)")
rootCmd.PersistentFlags().StringP("baseline-path", "b", "", "path to baseline with issues that can be ignored")
rootCmd.PersistentFlags().StringP("log-level", "l", "info", "log level (trace, debug, info, warn, error, fatal)")
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "show verbose output from scan")
rootCmd.PersistentFlags().Int("max-target-megabytes", 0, "files larger than this will be skipped")
rootCmd.PersistentFlags().Bool("redact", false, "redact secrets from logs and stdout")
rootCmd.PersistentFlags().Bool("no-banner", false, "suppress banner")
err := viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config"))
if err != nil {
log.Fatal().Msgf("err binding config %s", err.Error())
}
}
func initLog() {
zerolog.SetGlobalLevel(zerolog.InfoLevel)
ll, err := rootCmd.Flags().GetString("log-level")
if err != nil {
log.Fatal().Msg(err.Error())
}
switch strings.ToLower(ll) {
case "trace":
zerolog.SetGlobalLevel(zerolog.TraceLevel)
case "debug":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
case "info":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
case "warn":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
case "err", "error":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
case "fatal":
zerolog.SetGlobalLevel(zerolog.FatalLevel)
default:
zerolog.SetGlobalLevel(zerolog.InfoLevel)
}
}
func initConfig() {
hideBanner, err := rootCmd.Flags().GetBool("no-banner")
if err != nil {
log.Fatal().Msg(err.Error())
}
if !hideBanner {
_, _ = fmt.Fprint(os.Stderr, banner)
}
cfgPath, err := rootCmd.Flags().GetString("config")
if err != nil {
log.Fatal().Msg(err.Error())
}
if cfgPath != "" {
viper.SetConfigFile(cfgPath)
log.Debug().Msgf("using gitleaks config %s from `--config`", cfgPath)
} else if os.Getenv("GITLEAKS_CONFIG") != "" {
envPath := os.Getenv("GITLEAKS_CONFIG")
viper.SetConfigFile(envPath)
log.Debug().Msgf("using gitleaks config from GITLEAKS_CONFIG env var: %s", envPath)
} else {
source, err := rootCmd.Flags().GetString("source")
if err != nil {
log.Fatal().Msg(err.Error())
}
fileInfo, err := os.Stat(source)
if err != nil {
log.Fatal().Msg(err.Error())
}
if !fileInfo.IsDir() {
log.Debug().Msgf("unable to load gitleaks config from %s since --source=%s is a file, using default config",
filepath.Join(source, ".gitleaks.toml"), source)
viper.SetConfigType("toml")
if err = viper.ReadConfig(strings.NewReader(config.DefaultConfig)); err != nil {
log.Fatal().Msgf("err reading toml %s", err.Error())
}
return
}
if _, err := os.Stat(filepath.Join(source, ".gitleaks.toml")); os.IsNotExist(err) {
log.Debug().Msgf("no gitleaks config found in path %s, using default gitleaks config", filepath.Join(source, ".gitleaks.toml"))
viper.SetConfigType("toml")
if err = viper.ReadConfig(strings.NewReader(config.DefaultConfig)); err != nil {
log.Fatal().Msgf("err reading default config toml %s", err.Error())
}
return
} else {
log.Debug().Msgf("using existing gitleaks config %s from `(--source)/.gitleaks.toml`", filepath.Join(source, ".gitleaks.toml"))
}
viper.AddConfigPath(source)
viper.SetConfigName(".gitleaks")
viper.SetConfigType("toml")
}
if err := viper.ReadInConfig(); err != nil {
log.Fatal().Msgf("unable to load gitleaks config, err: %s", err)
}
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
if strings.Contains(err.Error(), "unknown flag") {
// exit code 126: Command invoked cannot execute
os.Exit(126)
}
log.Fatal().Msg(err.Error())
}
}
|