| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- package config
- import (
- "fmt"
- "io"
- "os"
- "path"
- "path/filepath"
- "regexp"
- "strconv"
- "github.com/zricethezav/gitleaks/v7/options"
- "github.com/BurntSushi/toml"
- "github.com/go-git/go-git/v5"
- log "github.com/sirupsen/logrus"
- )
- // Config is a composite struct of Rules and Allowlists
- // Each Rule contains a description, regular expression, tags, and allowlists if available
- type Config struct {
- Rules []Rule
- Allowlist AllowList
- }
- // Entropy represents an entropy range
- type Entropy struct {
- Min float64
- Max float64
- Group int
- }
- // TomlAllowList is a struct used in the TomlLoader that loads in allowlists from
- // specific rules or globally at the top level config
- type TomlAllowList struct {
- Description string
- Regexes []string
- Commits []string
- Files []string
- Paths []string
- Repos []string
- }
- // TomlLoader gets loaded with the values from a gitleaks toml config
- // see the config in config/defaults.go for an example. TomlLoader is used
- // to generate Config values (compiling regexes, etc).
- type TomlLoader struct {
- AllowList TomlAllowList
- Rules []struct {
- Description string
- Regex string
- File string
- Path string
- ReportGroup int
- Tags []string
- Entropies []struct {
- Min string
- Max string
- Group string
- }
- AllowList TomlAllowList
- }
- }
- // NewConfig will create a new config struct which contains
- // rules on how gitleaks will proceed with its scan.
- // If no options are passed via cli then NewConfig will return
- // a default config which can be seen in config.go
- func NewConfig(options options.Options) (Config, error) {
- var cfg Config
- tomlLoader := TomlLoader{}
- var err error
- if options.ConfigPath != "" {
- _, err = toml.DecodeFile(options.ConfigPath, &tomlLoader)
- // append a allowlist rule for allowlisting the config
- tomlLoader.AllowList.Files = append(tomlLoader.AllowList.Files, path.Base(options.ConfigPath))
- } else {
- _, err = toml.Decode(DefaultConfig, &tomlLoader)
- }
- if err != nil {
- return cfg, err
- }
- cfg, err = tomlLoader.Parse()
- if err != nil {
- return cfg, err
- }
- return cfg, nil
- }
- // Parse will parse the values set in a TomlLoader and use those values
- // to create compiled regular expressions and rules used in scans
- func (tomlLoader TomlLoader) Parse() (Config, error) {
- var cfg Config
- for _, rule := range tomlLoader.Rules {
- // check and make sure the rule is valid
- if rule.Regex == "" && rule.Path == "" && rule.File == "" && len(rule.Entropies) == 0 {
- log.Warnf("Rule %s does not define any actionable data", rule.Description)
- continue
- }
- re, err := regexp.Compile(rule.Regex)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- fileNameRe, err := regexp.Compile(rule.File)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- filePathRe, err := regexp.Compile(rule.Path)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- // rule specific allowlists
- var allowList AllowList
- allowList.Description = rule.AllowList.Description
- // rule specific regexes
- for _, re := range rule.AllowList.Regexes {
- allowListedRegex, err := regexp.Compile(re)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- allowList.Regexes = append(allowList.Regexes, allowListedRegex)
- }
- // rule specific filenames
- for _, re := range rule.AllowList.Files {
- allowListedRegex, err := regexp.Compile(re)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- allowList.Files = append(allowList.Files, allowListedRegex)
- }
- // rule specific paths
- for _, re := range rule.AllowList.Paths {
- allowListedRegex, err := regexp.Compile(re)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- allowList.Paths = append(allowList.Paths, allowListedRegex)
- }
- // rule specific commits
- allowList.Commits = rule.AllowList.Commits
- var entropies []Entropy
- for _, e := range rule.Entropies {
- min, err := strconv.ParseFloat(e.Min, 64)
- if err != nil {
- return cfg, err
- }
- max, err := strconv.ParseFloat(e.Max, 64)
- if err != nil {
- return cfg, err
- }
- if e.Group == "" {
- e.Group = "0"
- }
- group, err := strconv.ParseInt(e.Group, 10, 64)
- if err != nil {
- return cfg, err
- } else if int(group) >= len(re.SubexpNames()) {
- return cfg, fmt.Errorf("problem loading config: group cannot be higher than number of groups in regexp")
- } else if group < 0 {
- return cfg, fmt.Errorf("problem loading config: group cannot be lower than 0")
- } else if min > 8.0 || min < 0.0 || max > 8.0 || max < 0.0 {
- return cfg, fmt.Errorf("problem loading config: invalid entropy ranges, must be within 0.0-8.0")
- } else if min > max {
- return cfg, fmt.Errorf("problem loading config: entropy Min value cannot be higher than Max value")
- }
- entropies = append(entropies, Entropy{Min: min, Max: max, Group: int(group)})
- }
- r := Rule{
- Description: rule.Description,
- Regex: re,
- File: fileNameRe,
- Path: filePathRe,
- ReportGroup: rule.ReportGroup,
- Tags: rule.Tags,
- AllowList: allowList,
- Entropies: entropies,
- }
- cfg.Rules = append(cfg.Rules, r)
- }
- // global regex allowLists
- for _, allowListRegex := range tomlLoader.AllowList.Regexes {
- re, err := regexp.Compile(allowListRegex)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- cfg.Allowlist.Regexes = append(cfg.Allowlist.Regexes, re)
- }
- // global file name allowLists
- for _, allowListFileName := range tomlLoader.AllowList.Files {
- re, err := regexp.Compile(allowListFileName)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- cfg.Allowlist.Files = append(cfg.Allowlist.Files, re)
- }
- // global file path allowLists
- for _, allowListFilePath := range tomlLoader.AllowList.Paths {
- re, err := regexp.Compile(allowListFilePath)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- cfg.Allowlist.Paths = append(cfg.Allowlist.Paths, re)
- }
- // global repo allowLists
- for _, allowListRepo := range tomlLoader.AllowList.Repos {
- re, err := regexp.Compile(allowListRepo)
- if err != nil {
- return cfg, fmt.Errorf("problem loading config: %v", err)
- }
- cfg.Allowlist.Repos = append(cfg.Allowlist.Repos, re)
- }
- cfg.Allowlist.Commits = tomlLoader.AllowList.Commits
- cfg.Allowlist.Description = tomlLoader.AllowList.Description
- return cfg, nil
- }
- // LoadRepoConfig accepts a repo and config path related to the target repo's root.
- func LoadRepoConfig(repo *git.Repository, repoConfig string) (Config, error) {
- gitRepoConfig, err := repo.Config()
- if err != nil {
- return Config{}, err
- }
- if !gitRepoConfig.Core.IsBare {
- wt, err := repo.Worktree()
- if err != nil {
- return Config{}, err
- }
- _, err = wt.Filesystem.Stat(repoConfig)
- if err != nil {
- return Config{}, err
- }
- r, err := wt.Filesystem.Open(repoConfig)
- if err != nil {
- return Config{}, err
- }
- return parseTomlFile(r)
- }
- log.Debug("attempting to load repo config from bare worktree, this may use an old config")
- ref, err := repo.Head()
- if err != nil {
- return Config{}, err
- }
- c, err := repo.CommitObject(ref.Hash())
- if err != nil {
- return Config{}, err
- }
- f, err := c.File(repoConfig)
- if err != nil {
- return Config{}, err
- }
- r, err := f.Reader()
- if err != nil {
- return Config{}, err
- }
- return parseTomlFile(r)
- }
- // LoadAdditionalConfig Accepts a path to a gitleaks config and returns a Config struct
- func LoadAdditionalConfig(repoConfig string) (Config, error) {
- file, err := os.Open(filepath.Clean(repoConfig))
- if err != nil {
- return Config{}, err
- }
- return parseTomlFile(file)
- }
- // AppendConfig Accepts a Config struct and will append those fields to this Config Struct's fields
- func (config *Config) AppendConfig(configToBeAppended Config) Config {
- newAllowList := AllowList{
- Description: "Appended Configuration",
- Commits: append(config.Allowlist.Commits, configToBeAppended.Allowlist.Commits...),
- Files: append(config.Allowlist.Files, configToBeAppended.Allowlist.Files...),
- Paths: append(config.Allowlist.Paths, configToBeAppended.Allowlist.Paths...),
- Regexes: append(config.Allowlist.Regexes, configToBeAppended.Allowlist.Regexes...),
- Repos: append(config.Allowlist.Repos, configToBeAppended.Allowlist.Repos...),
- }
- return Config{
- Rules: append(config.Rules, configToBeAppended.Rules...),
- Allowlist: newAllowList,
- }
- }
- // takes a File, makes sure it is a valid config, and parses it
- func parseTomlFile(f io.Reader) (Config, error) {
- var tomlLoader TomlLoader
- _, err := toml.DecodeReader(f, &tomlLoader)
- if err != nil {
- log.Errorf("Unable to read gitleaks config. Using defaults. Error: %s", err)
- return Config{}, err
- }
- return tomlLoader.Parse()
- }
|