Pārlūkot izejas kodu

Feature: Adding the ability to extend configuration files (#926)

* init

* working on default and path config extensions

* adding trace log level, consolidating some code

* cleaning things up, updating generate package

* fix tests

* formatting

* adding tests for extend

* extend not extends

* formatting

* only allow usedefault or path to be set

* update readme

* add note about allowlists

* more readme, expand env var for path

* actually dont support env var. ez attack
Zachary Rice 3 gadi atpakaļ
vecāks
revīzija
31650f01e7

+ 19 - 2
README.md

@@ -17,7 +17,7 @@
 		  <img src="https://img.shields.io/docker/pulls/zricethezav/gitleaks.svg" />
 		  <img src="https://img.shields.io/docker/pulls/zricethezav/gitleaks.svg" />
 	  </a>
 	  </a>
 	  <a href="https://www.jit.io/jit-open-source-gitleaks?utm_source=github&utm_medium=badge&utm_campaign=GitleaksReadme&utm_id=oss&items=item-secret-detection">
 	  <a href="https://www.jit.io/jit-open-source-gitleaks?utm_source=github&utm_medium=badge&utm_campaign=GitleaksReadme&utm_id=oss&items=item-secret-detection">
-<img src="https://img.shields.io/badge/Secured%20by-Jit-B8287F?style=?style=plastic" /> 
+<img src="https://img.shields.io/badge/Secured%20by-Jit-B8287F?style=?style=plastic" />
 	  </a>
 	  </a>
 	  <a href="https://github.com/zricethezav/gitleaks-action">
 	  <a href="https://github.com/zricethezav/gitleaks-action">
         	<img alt="gitleaks badge" src="https://img.shields.io/badge/protected%20by-gitleaks-blue">
         	<img alt="gitleaks badge" src="https://img.shields.io/badge/protected%20by-gitleaks-blue">
@@ -28,7 +28,7 @@
   </p>
   </p>
 </p>
 </p>
 
 
-Gitleaks is a SAST tool for **detecting** and **preventing** hardcoded secrets like passwords, api keys, and tokens in git repos. Gitleaks is an **easy-to-use, all-in-one solution** for detecting secrets, past or present, in your code. 
+Gitleaks is a SAST tool for **detecting** and **preventing** hardcoded secrets like passwords, api keys, and tokens in git repos. Gitleaks is an **easy-to-use, all-in-one solution** for detecting secrets, past or present, in your code.
 
 
 | Demos |
 | Demos |
 | ----------- |
 | ----------- |
@@ -258,6 +258,23 @@ Gitleaks offers a configuration format you can follow to write your own secret d
 # Title for the gitleaks configuration file.
 # Title for the gitleaks configuration file.
 title = "Gitleaks title"
 title = "Gitleaks title"
 
 
+# Extend the base (this) configuration. When you extend a configuration
+# the base rules take precendence over the extended rules. I.e, if there are
+# duplicate rules in both the base configuration and the extended configuration
+# the base rules will override the extended rules.
+# Another thing to know with extending configurations is you can chain together
+# multiple configuration files to a depth of 2. Allowlist arrays are appended
+# and can contain duplicates.
+# useDefault and path can NOT be used at the same time. Choose one.
+[extend]
+# useDefault will extend the base configuration with the default gitleaks config
+useDefault = true
+# or you can supply a path to a configuration. Path is relative to where gitleaks
+# was invoked, not the location of the base config. This is a bit clunky.
+# A better alternative would be to supply an absolute path (expanded env var
+# not supported at this time)
+path = "common_config.toml"
+
 # An array of tables that contain information that define instructions
 # An array of tables that contain information that define instructions
 # on how to detect secrets
 # on how to detect secrets
 [[rules]]
 [[rules]]

+ 5 - 3
cmd/generate/config/main.go

@@ -149,16 +149,18 @@ func main() {
 	configRules = append(configRules, rules.GenericCredential())
 	configRules = append(configRules, rules.GenericCredential())
 
 
 	// ensure rules have unique ids
 	// ensure rules have unique ids
-	ruleLookUp := make(map[string]bool)
+	ruleLookUp := make(map[string]config.Rule)
 	for _, rule := range configRules {
 	for _, rule := range configRules {
 		// check if rule is in ruleLookUp
 		// check if rule is in ruleLookUp
 		if _, ok := ruleLookUp[rule.RuleID]; ok {
 		if _, ok := ruleLookUp[rule.RuleID]; ok {
 			log.Fatal().Msgf("rule id %s is not unique", rule.RuleID)
 			log.Fatal().Msgf("rule id %s is not unique", rule.RuleID)
 		}
 		}
-		ruleLookUp[rule.RuleID] = true
+		// TODO: eventually change all the signatures to get ride of this
+		// nasty dereferencing.
+		ruleLookUp[rule.RuleID] = *rule
 	}
 	}
 	config := config.Config{
 	config := config.Config{
-		Rules: configRules,
+		Rules: ruleLookUp,
 	}
 	}
 	tmpl, err := template.ParseFiles(templatePath)
 	tmpl, err := template.ParseFiles(templatePath)
 	if err != nil {
 	if err != nil {

+ 3 - 1
cmd/generate/config/rules/rule.go

@@ -62,8 +62,10 @@ func validate(r config.Rule, truePositives []string, falsePositives []string) *c
 	}
 	}
 	r.Keywords = keywords
 	r.Keywords = keywords
 
 
+	rules := make(map[string]config.Rule)
+	rules[r.RuleID] = r
 	d := detect.NewDetector(config.Config{
 	d := detect.NewDetector(config.Config{
-		Rules:    []*config.Rule{&r},
+		Rules:    rules,
 		Keywords: keywords,
 		Keywords: keywords,
 	})
 	})
 	for _, tp := range truePositives {
 	for _, tp := range truePositives {

+ 12 - 10
cmd/root.go

@@ -19,13 +19,13 @@ const banner = `
     │╲
     │╲
     │ ○
     │ ○
     ○ ░
     ○ ░
-    ░    gitleaks 
+    ░    gitleaks
 
 
 `
 `
 
 
 const configDescription = `config file path
 const configDescription = `config file path
-order of precedence: 
-1. --config/-c 
+order of precedence:
+1. --config/-c
 2. env var GITLEAKS_CONFIG
 2. env var GITLEAKS_CONFIG
 3. (--source/-s)/.gitleaks.toml
 3. (--source/-s)/.gitleaks.toml
 If none of the three options are used, then gitleaks will use the default config`
 If none of the three options are used, then gitleaks will use the default config`
@@ -42,7 +42,7 @@ func init() {
 	rootCmd.PersistentFlags().StringP("source", "s", ".", "path to source (default: $PWD)")
 	rootCmd.PersistentFlags().StringP("source", "s", ".", "path to source (default: $PWD)")
 	rootCmd.PersistentFlags().StringP("report-path", "r", "", "report file")
 	rootCmd.PersistentFlags().StringP("report-path", "r", "", "report file")
 	rootCmd.PersistentFlags().StringP("report-format", "f", "json", "output format (json, csv, sarif)")
 	rootCmd.PersistentFlags().StringP("report-format", "f", "json", "output format (json, csv, sarif)")
-	rootCmd.PersistentFlags().StringP("log-level", "l", "info", "log level (debug, info, warn, error, fatal)")
+	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().BoolP("verbose", "v", false, "show verbose output from scan")
 	rootCmd.PersistentFlags().Bool("redact", false, "redact secrets from logs and stdout")
 	rootCmd.PersistentFlags().Bool("redact", false, "redact secrets from logs and stdout")
 	err := viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config"))
 	err := viper.BindPFlag("config", rootCmd.PersistentFlags().Lookup("config"))
@@ -58,6 +58,8 @@ func initLog() {
 		log.Fatal().Msg(err.Error())
 		log.Fatal().Msg(err.Error())
 	}
 	}
 	switch strings.ToLower(ll) {
 	switch strings.ToLower(ll) {
+	case "trace":
+		zerolog.SetGlobalLevel(zerolog.TraceLevel)
 	case "debug":
 	case "debug":
 		zerolog.SetGlobalLevel(zerolog.DebugLevel)
 		zerolog.SetGlobalLevel(zerolog.DebugLevel)
 	case "info":
 	case "info":
@@ -81,11 +83,11 @@ func initConfig() {
 	}
 	}
 	if cfgPath != "" {
 	if cfgPath != "" {
 		viper.SetConfigFile(cfgPath)
 		viper.SetConfigFile(cfgPath)
-		log.Debug().Msgf("Using gitleaks config %s from `--config`", cfgPath)
+		log.Debug().Msgf("using gitleaks config %s from `--config`", cfgPath)
 	} else if os.Getenv("GITLEAKS_CONFIG") != "" {
 	} else if os.Getenv("GITLEAKS_CONFIG") != "" {
 		envPath := os.Getenv("GITLEAKS_CONFIG")
 		envPath := os.Getenv("GITLEAKS_CONFIG")
 		viper.SetConfigFile(envPath)
 		viper.SetConfigFile(envPath)
-		log.Debug().Msgf("Using gitleaks config from GITLEAKS_CONFIG env var: %s", envPath)
+		log.Debug().Msgf("using gitleaks config from GITLEAKS_CONFIG env var: %s", envPath)
 	} else {
 	} else {
 		source, err := rootCmd.Flags().GetString("source")
 		source, err := rootCmd.Flags().GetString("source")
 		if err != nil {
 		if err != nil {
@@ -97,7 +99,7 @@ func initConfig() {
 		}
 		}
 
 
 		if !fileInfo.IsDir() {
 		if !fileInfo.IsDir() {
-			log.Debug().Msgf("Unable to load gitleaks config from %s since --source=%s is a file, using default config",
+			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)
 				filepath.Join(source, ".gitleaks.toml"), source)
 			viper.SetConfigType("toml")
 			viper.SetConfigType("toml")
 			if err = viper.ReadConfig(strings.NewReader(config.DefaultConfig)); err != nil {
 			if err = viper.ReadConfig(strings.NewReader(config.DefaultConfig)); err != nil {
@@ -107,14 +109,14 @@ func initConfig() {
 		}
 		}
 
 
 		if _, err := os.Stat(filepath.Join(source, ".gitleaks.toml")); os.IsNotExist(err) {
 		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"))
+			log.Debug().Msgf("no gitleaks config found in path %s, using default gitleaks config", filepath.Join(source, ".gitleaks.toml"))
 			viper.SetConfigType("toml")
 			viper.SetConfigType("toml")
 			if err = viper.ReadConfig(strings.NewReader(config.DefaultConfig)); err != nil {
 			if err = viper.ReadConfig(strings.NewReader(config.DefaultConfig)); err != nil {
 				log.Fatal().Msgf("err reading default config toml %s", err.Error())
 				log.Fatal().Msgf("err reading default config toml %s", err.Error())
 			}
 			}
 			return
 			return
 		} else {
 		} else {
-			log.Debug().Msgf("Using existing gitleaks config %s from `(--source)/.gitleaks.toml`", filepath.Join(source, ".gitleaks.toml"))
+			log.Debug().Msgf("using existing gitleaks config %s from `(--source)/.gitleaks.toml`", filepath.Join(source, ".gitleaks.toml"))
 		}
 		}
 
 
 		viper.AddConfigPath(source)
 		viper.AddConfigPath(source)
@@ -122,7 +124,7 @@ func initConfig() {
 		viper.SetConfigType("toml")
 		viper.SetConfigType("toml")
 	}
 	}
 	if err := viper.ReadInConfig(); err != nil {
 	if err := viper.ReadInConfig(); err != nil {
-		log.Fatal().Msgf("Unable to load gitleaks config, err: %s", err)
+		log.Fatal().Msgf("unable to load gitleaks config, err: %s", err)
 	}
 	}
 }
 }
 
 

+ 127 - 9
config/config.go

@@ -5,16 +5,26 @@ import (
 	"fmt"
 	"fmt"
 	"regexp"
 	"regexp"
 	"strings"
 	"strings"
+
+	"github.com/rs/zerolog/log"
+	"github.com/spf13/viper"
 )
 )
 
 
 //go:embed gitleaks.toml
 //go:embed gitleaks.toml
 var DefaultConfig string
 var DefaultConfig string
 
 
+// use to keep track of how many configs we can extend
+// yea I know, globals bad
+var extendDepth int
+
+const maxExtendDepth = 2
+
 // ViperConfig is the config struct used by the Viper config package
 // ViperConfig is the config struct used by the Viper config package
 // to parse the config file. This struct does not include regular expressions.
 // to parse the config file. This struct does not include regular expressions.
 // It is used as an intermediary to convert the Viper config to the Config struct.
 // It is used as an intermediary to convert the Viper config to the Config struct.
 type ViperConfig struct {
 type ViperConfig struct {
 	Description string
 	Description string
+	Extend      Extend
 	Rules       []struct {
 	Rules       []struct {
 		ID          string
 		ID          string
 		Description string
 		Description string
@@ -42,18 +52,32 @@ type ViperConfig struct {
 
 
 // Config is a configuration struct that contains rules and an allowlist if present.
 // Config is a configuration struct that contains rules and an allowlist if present.
 type Config struct {
 type Config struct {
+	Extend      Extend
 	Path        string
 	Path        string
 	Description string
 	Description string
-	Rules       []*Rule
+	Rules       map[string]Rule
 	Allowlist   Allowlist
 	Allowlist   Allowlist
 	Keywords    []string
 	Keywords    []string
+
+	// used to keep sarif results consistent
+	orderedRules []string
+}
+
+// Extend is a struct that allows users to define how they want their
+// configuration extended by other configuration files.
+type Extend struct {
+	Path       string
+	URL        string
+	UseDefault bool
 }
 }
 
 
 func (vc *ViperConfig) Translate() (Config, error) {
 func (vc *ViperConfig) Translate() (Config, error) {
 	var (
 	var (
-		rules    []*Rule
-		keywords []string
+		keywords     []string
+		orderedRules []string
 	)
 	)
+	rulesMap := make(map[string]Rule)
+
 	for _, r := range vc.Rules {
 	for _, r := range vc.Rules {
 		var allowlistRegexes []*regexp.Regexp
 		var allowlistRegexes []*regexp.Regexp
 		for _, a := range r.Allowlist.Regexes {
 		for _, a := range r.Allowlist.Regexes {
@@ -88,7 +112,7 @@ func (vc *ViperConfig) Translate() (Config, error) {
 		} else {
 		} else {
 			configPathRegex = regexp.MustCompile(r.Path)
 			configPathRegex = regexp.MustCompile(r.Path)
 		}
 		}
-		r := &Rule{
+		r := Rule{
 			Description: r.Description,
 			Description: r.Description,
 			RuleID:      r.ID,
 			RuleID:      r.ID,
 			Regex:       configRegex,
 			Regex:       configRegex,
@@ -104,10 +128,12 @@ func (vc *ViperConfig) Translate() (Config, error) {
 				StopWords: r.Allowlist.StopWords,
 				StopWords: r.Allowlist.StopWords,
 			},
 			},
 		}
 		}
+		orderedRules = append(orderedRules, r.RuleID)
+
 		if r.Regex != nil && r.SecretGroup > r.Regex.NumSubexp() {
 		if r.Regex != nil && r.SecretGroup > r.Regex.NumSubexp() {
 			return Config{}, fmt.Errorf("%s invalid regex secret group %d, max regex secret group %d", r.Description, r.SecretGroup, r.Regex.NumSubexp())
 			return Config{}, fmt.Errorf("%s invalid regex secret group %d, max regex secret group %d", r.Description, r.SecretGroup, r.Regex.NumSubexp())
 		}
 		}
-		rules = append(rules, r)
+		rulesMap[r.RuleID] = r
 	}
 	}
 	var allowlistRegexes []*regexp.Regexp
 	var allowlistRegexes []*regexp.Regexp
 	for _, a := range vc.Allowlist.Regexes {
 	for _, a := range vc.Allowlist.Regexes {
@@ -117,15 +143,107 @@ func (vc *ViperConfig) Translate() (Config, error) {
 	for _, a := range vc.Allowlist.Paths {
 	for _, a := range vc.Allowlist.Paths {
 		allowlistPaths = append(allowlistPaths, regexp.MustCompile(a))
 		allowlistPaths = append(allowlistPaths, regexp.MustCompile(a))
 	}
 	}
-	return Config{
+	c := Config{
 		Description: vc.Description,
 		Description: vc.Description,
-		Rules:       rules,
+		Extend:      vc.Extend,
+		Rules:       rulesMap,
 		Allowlist: Allowlist{
 		Allowlist: Allowlist{
 			Regexes:   allowlistRegexes,
 			Regexes:   allowlistRegexes,
 			Paths:     allowlistPaths,
 			Paths:     allowlistPaths,
 			Commits:   vc.Allowlist.Commits,
 			Commits:   vc.Allowlist.Commits,
 			StopWords: vc.Allowlist.StopWords,
 			StopWords: vc.Allowlist.StopWords,
 		},
 		},
-		Keywords: keywords,
-	}, nil
+		Keywords:     keywords,
+		orderedRules: orderedRules,
+	}
+
+	if maxExtendDepth != extendDepth {
+		// disallow both usedefault and path from being set
+		if c.Extend.Path != "" && c.Extend.UseDefault {
+			log.Fatal().Msg("unable to load config due to extend.path and extend.useDefault being set")
+		}
+		if c.Extend.UseDefault {
+			c.extendDefault()
+		} else if c.Extend.Path != "" {
+			c.extendPath()
+		}
+
+	}
+
+	return c, nil
+}
+
+func (c *Config) OrderedRules() []Rule {
+	var orderedRules []Rule
+	for _, id := range c.orderedRules {
+		if _, ok := c.Rules[id]; ok {
+			orderedRules = append(orderedRules, c.Rules[id])
+		}
+	}
+	return orderedRules
+}
+
+func (c *Config) extendDefault() {
+	extendDepth++
+	viper.SetConfigType("toml")
+	if err := viper.ReadConfig(strings.NewReader(DefaultConfig)); err != nil {
+		log.Fatal().Msgf("failed to load extended config, err: %s", err)
+		return
+	}
+	defaultViperConfig := ViperConfig{}
+	if err := viper.Unmarshal(&defaultViperConfig); err != nil {
+		log.Fatal().Msgf("failed to load extended config, err: %s", err)
+		return
+	}
+	cfg, err := defaultViperConfig.Translate()
+	if err != nil {
+		log.Fatal().Msgf("failed to load extended config, err: %s", err)
+		return
+	}
+	log.Debug().Msg("extending config with default config")
+	c.extend(cfg)
+
+}
+
+func (c *Config) extendPath() {
+	extendDepth++
+	viper.SetConfigFile(c.Extend.Path)
+	if err := viper.ReadInConfig(); err != nil {
+		log.Fatal().Msgf("failed to load extended config, err: %s", err)
+		return
+	}
+	extensionViperConfig := ViperConfig{}
+	if err := viper.Unmarshal(&extensionViperConfig); err != nil {
+		log.Fatal().Msgf("failed to load extended config, err: %s", err)
+		return
+	}
+	cfg, err := extensionViperConfig.Translate()
+	if err != nil {
+		log.Fatal().Msgf("failed to load extended config, err: %s", err)
+		return
+	}
+	log.Debug().Msgf("extending config with %s", c.Extend.Path)
+	c.extend(cfg)
+}
+
+func (c *Config) extendURL() {
+	// TODO
+}
+
+func (c *Config) extend(extensionConfig Config) {
+	for ruleID, rule := range extensionConfig.Rules {
+		if _, ok := c.Rules[ruleID]; !ok {
+			log.Trace().Msgf("adding %s to base config", ruleID)
+			c.Rules[ruleID] = rule
+			c.Keywords = append(c.Keywords, rule.Keywords...)
+		}
+	}
+
+	// append allowlists, not attempting to merge
+	c.Allowlist.Commits = append(c.Allowlist.Commits,
+		extensionConfig.Allowlist.Commits...)
+	c.Allowlist.Paths = append(c.Allowlist.Paths,
+		extensionConfig.Allowlist.Paths...)
+	c.Allowlist.Regexes = append(c.Allowlist.Regexes,
+		extensionConfig.Allowlist.Regexes...)
 }
 }

+ 67 - 43
config/config_test.go

@@ -20,72 +20,68 @@ func TestTranslate(t *testing.T) {
 		{
 		{
 			cfgName: "allow_aws_re",
 			cfgName: "allow_aws_re",
 			cfg: Config{
 			cfg: Config{
-				Rules: []*Rule{
-					{
-						Description: "AWS Access Key",
-						Regex:       regexp.MustCompile("(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}"),
-						Tags:        []string{"key", "AWS"},
-						Keywords:    []string{},
-						RuleID:      "aws-access-key",
-						Allowlist: Allowlist{
-							Regexes: []*regexp.Regexp{
-								regexp.MustCompile("AKIALALEMEL33243OLIA"),
-							},
+				Rules: map[string]Rule{"aws-access-key": {
+					Description: "AWS Access Key",
+					Regex:       regexp.MustCompile("(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}"),
+					Tags:        []string{"key", "AWS"},
+					Keywords:    []string{},
+					RuleID:      "aws-access-key",
+					Allowlist: Allowlist{
+						Regexes: []*regexp.Regexp{
+							regexp.MustCompile("AKIALALEMEL33243OLIA"),
 						},
 						},
 					},
 					},
 				},
 				},
+				},
 			},
 			},
 		},
 		},
 		{
 		{
 			cfgName: "allow_commit",
 			cfgName: "allow_commit",
 			cfg: Config{
 			cfg: Config{
-				Rules: []*Rule{
-					{
-						Description: "AWS Access Key",
-						Regex:       regexp.MustCompile("(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}"),
-						Tags:        []string{"key", "AWS"},
-						Keywords:    []string{},
-						RuleID:      "aws-access-key",
-						Allowlist: Allowlist{
-							Commits: []string{"allowthiscommit"},
-						},
+				Rules: map[string]Rule{"aws-access-key": {
+					Description: "AWS Access Key",
+					Regex:       regexp.MustCompile("(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}"),
+					Tags:        []string{"key", "AWS"},
+					Keywords:    []string{},
+					RuleID:      "aws-access-key",
+					Allowlist: Allowlist{
+						Commits: []string{"allowthiscommit"},
 					},
 					},
 				},
 				},
+				},
 			},
 			},
 		},
 		},
 		{
 		{
 			cfgName: "allow_path",
 			cfgName: "allow_path",
 			cfg: Config{
 			cfg: Config{
-				Rules: []*Rule{
-					{
-						Description: "AWS Access Key",
-						Regex:       regexp.MustCompile("(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}"),
-						Tags:        []string{"key", "AWS"},
-						Keywords:    []string{},
-						RuleID:      "aws-access-key",
-						Allowlist: Allowlist{
-							Paths: []*regexp.Regexp{
-								regexp.MustCompile(".go"),
-							},
+				Rules: map[string]Rule{"aws-access-key": {
+					Description: "AWS Access Key",
+					Regex:       regexp.MustCompile("(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}"),
+					Tags:        []string{"key", "AWS"},
+					Keywords:    []string{},
+					RuleID:      "aws-access-key",
+					Allowlist: Allowlist{
+						Paths: []*regexp.Regexp{
+							regexp.MustCompile(".go"),
 						},
 						},
 					},
 					},
 				},
 				},
+				},
 			},
 			},
 		},
 		},
 		{
 		{
 			cfgName: "entropy_group",
 			cfgName: "entropy_group",
 			cfg: Config{
 			cfg: Config{
-				Rules: []*Rule{
-					{
-						Description: "Discord API key",
-						Regex:       regexp.MustCompile(`(?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{64})['\"]`),
-						RuleID:      "discord-api-key",
-						Allowlist:   Allowlist{},
-						Entropy:     3.5,
-						SecretGroup: 3,
-						Tags:        []string{},
-						Keywords:    []string{},
-					},
+				Rules: map[string]Rule{"discord-api-key": {
+					Description: "Discord API key",
+					Regex:       regexp.MustCompile(`(?i)(discord[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}['\"]([a-h0-9]{64})['\"]`),
+					RuleID:      "discord-api-key",
+					Allowlist:   Allowlist{},
+					Entropy:     3.5,
+					SecretGroup: 3,
+					Tags:        []string{},
+					Keywords:    []string{},
+				},
 				},
 				},
 			},
 			},
 		},
 		},
@@ -94,6 +90,34 @@ func TestTranslate(t *testing.T) {
 			cfg:       Config{},
 			cfg:       Config{},
 			wantError: fmt.Errorf("Discord API key invalid regex secret group 5, max regex secret group 3"),
 			wantError: fmt.Errorf("Discord API key invalid regex secret group 5, max regex secret group 3"),
 		},
 		},
+		{
+			cfgName: "base",
+			cfg: Config{
+				Rules: map[string]Rule{
+					"aws-access-key": {
+						Description: "AWS Access Key",
+						Regex:       regexp.MustCompile("(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}"),
+						Tags:        []string{"key", "AWS"},
+						Keywords:    []string{},
+						RuleID:      "aws-access-key",
+					},
+					"aws-secret-key": {
+						Description: "AWS Secret Key",
+						Regex:       regexp.MustCompile(`(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}`),
+						Tags:        []string{"key", "AWS"},
+						Keywords:    []string{},
+						RuleID:      "aws-secret-key",
+					},
+					"aws-secret-key-again": {
+						Description: "AWS Secret Key",
+						Regex:       regexp.MustCompile(`(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}`),
+						Tags:        []string{"key", "AWS"},
+						Keywords:    []string{},
+						RuleID:      "aws-secret-key-again",
+					},
+				},
+			},
+		},
 	}
 	}
 
 
 	for _, tt := range tests {
 	for _, tt := range tests {

+ 887 - 887
config/gitleaks.toml

@@ -123,6 +123,15 @@ keywords = [
     "akia","agpa","aida","aroa","aipa","anpa","anva","asia",
     "akia","agpa","aida","aroa","aipa","anpa","anva","asia",
 ]
 ]
 
 
+[[rules]]
+description = "Beamer API token"
+id = "beamer-api-token"
+regex = '''(?i)(?:beamer)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(b_[a-z0-9=_\-]{44})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "beamer",
+]
+
 [[rules]]
 [[rules]]
 description = "Bitbucket Client ID"
 description = "Bitbucket Client ID"
 id = "bitbucket-client-id"
 id = "bitbucket-client-id"
@@ -160,12 +169,11 @@ keywords = [
 ]
 ]
 
 
 [[rules]]
 [[rules]]
-description = "Beamer API token"
-id = "beamer-api-token"
-regex = '''(?i)(?:beamer)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(b_[a-z0-9=_\-]{44})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
+description = "Clojars API token"
+id = "clojars-api-token"
+regex = '''(?i)(CLOJARS_)[a-z0-9]{60}'''
 keywords = [
 keywords = [
-    "beamer",
+    "clojars",
 ]
 ]
 
 
 [[rules]]
 [[rules]]
@@ -186,14 +194,6 @@ keywords = [
     "coinbase",
     "coinbase",
 ]
 ]
 
 
-[[rules]]
-description = "Clojars API token"
-id = "clojars-api-token"
-regex = '''(?i)(CLOJARS_)[a-z0-9]{60}'''
-keywords = [
-    "clojars",
-]
-
 [[rules]]
 [[rules]]
 description = "Confluent Access Token"
 description = "Confluent Access Token"
 id = "confluent-access-token"
 id = "confluent-access-token"
@@ -273,6 +273,15 @@ keywords = [
     "doppler",
     "doppler",
 ]
 ]
 
 
+[[rules]]
+description = "Droneci Access Token"
+id = "droneci-access-token"
+regex = '''(?i)(?:droneci)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "droneci",
+]
+
 [[rules]]
 [[rules]]
 description = "Dropbox API secret"
 description = "Dropbox API secret"
 id = "dropbox-api-token"
 id = "dropbox-api-token"
@@ -298,15 +307,6 @@ keywords = [
     "dropbox",
     "dropbox",
 ]
 ]
 
 
-[[rules]]
-description = "Droneci Access Token"
-id = "droneci-access-token"
-regex = '''(?i)(?:droneci)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "droneci",
-]
-
 [[rules]]
 [[rules]]
 description = "Duffel API token"
 description = "Duffel API token"
 id = "duffel-api-token"
 id = "duffel-api-token"
@@ -366,6 +366,15 @@ keywords = [
     "fastly",
     "fastly",
 ]
 ]
 
 
+[[rules]]
+description = "Finicity API token"
+id = "finicity-api-token"
+regex = '''(?i)(?:finicity)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "finicity",
+]
+
 [[rules]]
 [[rules]]
 description = "Finicity Client Secret"
 description = "Finicity Client Secret"
 id = "finicity-client-secret"
 id = "finicity-client-secret"
@@ -376,12 +385,12 @@ keywords = [
 ]
 ]
 
 
 [[rules]]
 [[rules]]
-description = "Finicity API token"
-id = "finicity-api-token"
-regex = '''(?i)(?:finicity)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
+description = "Finnhub Access Token"
+id = "finnhub-access-token"
+regex = '''(?i)(?:finnhub)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{20})(?:['|\"|\n|\r|\s|\x60]|$)'''
 secretGroup = 1
 secretGroup = 1
 keywords = [
 keywords = [
-    "finicity",
+    "finnhub",
 ]
 ]
 
 
 [[rules]]
 [[rules]]
@@ -394,12 +403,11 @@ keywords = [
 ]
 ]
 
 
 [[rules]]
 [[rules]]
-description = "Finnhub Access Token"
-id = "finnhub-access-token"
-regex = '''(?i)(?:finnhub)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{20})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
+description = "Flutterwave Encryption Key"
+id = "flutterwave-encryption-key"
+regex = '''FLWSECK_TEST-(?i)[a-h0-9]{12}'''
 keywords = [
 keywords = [
-    "finnhub",
+    "flwseck_test",
 ]
 ]
 
 
 [[rules]]
 [[rules]]
@@ -418,14 +426,6 @@ keywords = [
     "flwseck_test",
     "flwseck_test",
 ]
 ]
 
 
-[[rules]]
-description = "Flutterwave Encryption Key"
-id = "flutterwave-encryption-key"
-regex = '''FLWSECK_TEST-(?i)[a-h0-9]{12}'''
-keywords = [
-    "flwseck_test",
-]
-
 [[rules]]
 [[rules]]
 description = "Frame.io API token"
 description = "Frame.io API token"
 id = "frameio-api-token"
 id = "frameio-api-token"
@@ -443,15 +443,6 @@ keywords = [
     "freshbooks",
     "freshbooks",
 ]
 ]
 
 
-[[rules]]
-description = "GoCardless API token"
-id = "gocardless-api-token"
-regex = '''(?i)(?:gocardless)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(live_(?i)[a-z0-9\-_=]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "live_","gocardless",
-]
-
 [[rules]]
 [[rules]]
 description = "GCP API key"
 description = "GCP API key"
 id = "gcp-api-key"
 id = "gcp-api-key"
@@ -462,688 +453,13 @@ keywords = [
 ]
 ]
 
 
 [[rules]]
 [[rules]]
-description = "GitHub Personal Access Token"
-id = "github-pat"
-regex = '''ghp_[0-9a-zA-Z]{36}'''
-keywords = [
-    "ghp_",
-]
-
-[[rules]]
-description = "GitHub OAuth Access Token"
-id = "github-oauth"
-regex = '''gho_[0-9a-zA-Z]{36}'''
-keywords = [
-    "gho_",
-]
-
-[[rules]]
-description = "GitHub App Token"
-id = "github-app-token"
-regex = '''(ghu|ghs)_[0-9a-zA-Z]{36}'''
-keywords = [
-    "ghu_","ghs_",
-]
-
-[[rules]]
-description = "GitHub Refresh Token"
-id = "github-refresh-token"
-regex = '''ghr_[0-9a-zA-Z]{36}'''
-keywords = [
-    "ghr_",
-]
-
-[[rules]]
-description = "GitLab Personal Access Token"
-id = "gitlab-pat"
-regex = '''glpat-[0-9a-zA-Z\-\_]{20}'''
-keywords = [
-    "glpat-",
-]
-
-[[rules]]
-description = "Gitter Access Token"
-id = "gitter-access-token"
-regex = '''(?i)(?:gitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9_-]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "gitter",
-]
-
-[[rules]]
-description = "HashiCorp Terraform user/org API token"
-id = "hashicorp-tf-api-token"
-regex = '''(?i)[a-z0-9]{14}\.atlasv1\.[a-z0-9\-_=]{60,70}'''
-keywords = [
-    "atlasv1",
-]
-
-[[rules]]
-description = "Heroku API Key"
-id = "heroku-api-key"
-regex = '''(?i)(?:heroku)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "heroku",
-]
-
-[[rules]]
-description = "HubSpot API Token"
-id = "hubspot-api-key"
-regex = '''(?i)(?:hubspot)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "hubspot",
-]
-
-[[rules]]
-description = "Intercom API Token"
-id = "intercom-api-key"
-regex = '''(?i)(?:intercom)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{60})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "intercom",
-]
-
-[[rules]]
-description = "Kraken Access Token"
-id = "kraken-access-token"
-regex = '''(?i)(?:kraken)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9\/=_\+\-]{80,90})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "kraken",
-]
-
-[[rules]]
-description = "Kucoin Access Token"
-id = "kucoin-access-token"
-regex = '''(?i)(?:kucoin)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{24})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "kucoin",
-]
-
-[[rules]]
-description = "Kucoin Secret Key"
-id = "kucoin-secret-key"
-regex = '''(?i)(?:kucoin)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "kucoin",
-]
-
-[[rules]]
-description = "Launchdarkly Access Token"
-id = "launchdarkly-access-token"
-regex = '''(?i)(?:launchdarkly)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "launchdarkly",
-]
-
-[[rules]]
-description = "Linear API Token"
-id = "linear-api-key"
-regex = '''lin_api_(?i)[a-z0-9]{40}'''
-keywords = [
-    "lin_api_",
-]
-
-[[rules]]
-description = "Linear Client Secret"
-id = "linear-client-secret"
-regex = '''(?i)(?:linear)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "linear",
-]
-
-[[rules]]
-description = "LinkedIn Client ID"
-id = "linkedin-client-id"
-regex = '''(?i)(?:linkedin|linked-in)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{14})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "linkedin","linked-in",
-]
-
-[[rules]]
-description = "LinkedIn Client secret"
-id = "linkedin-client-secret"
-regex = '''(?i)(?:linkedin|linked-in)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "linkedin","linked-in",
-]
-
-[[rules]]
-description = "Lob API Key"
-id = "lob-api-key"
-regex = '''(?i)(?:lob)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}((live|test)_[a-f0-9]{35})(?:['|\"|\n|\r|\s|\x60]|$)'''
+description = "Generic API Key"
+id = "generic-api-key"
+regex = '''(?i)(?:key|api|token|secret|client|passwd|password|auth|access)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-z\-_.=]{10,150})(?:['|\"|\n|\r|\s|\x60]|$)'''
 secretGroup = 1
 secretGroup = 1
+entropy = 3.5
 keywords = [
 keywords = [
-    "test_","live_",
-]
-
-[[rules]]
-description = "Lob Publishable API Key"
-id = "lob-pub-api-key"
-regex = '''(?i)(?:lob)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}((test|live)_pub_[a-f0-9]{31})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "test_pub","live_pub","_pub",
-]
-
-[[rules]]
-description = "Mailchimp API key"
-id = "mailchimp-api-key"
-regex = '''(?i)(?:mailchimp)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32}-us20)(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "mailchimp",
-]
-
-[[rules]]
-description = "Mailgun public validation key"
-id = "mailgun-pub-key"
-regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(pubkey-[a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "mailgun",
-]
-
-[[rules]]
-description = "Mailgun private API token"
-id = "mailgun-private-api-token"
-regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(key-[a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "mailgun",
-]
-
-[[rules]]
-description = "Mailgun webhook signing key"
-id = "mailgun-signing-key"
-regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-h0-9]{32}-[a-h0-9]{8}-[a-h0-9]{8})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "mailgun",
-]
-
-[[rules]]
-description = "MapBox API token"
-id = "mapbox-api-token"
-regex = '''(?i)(?:mapbox)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(pk\.[a-z0-9]{60}\.[a-z0-9]{22})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "mapbox",
-]
-
-[[rules]]
-description = "Mattermost Access Token"
-id = "mattermost-access-token"
-regex = '''(?i)(?:mattermost)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{26})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "mattermost",
-]
-
-[[rules]]
-description = "MessageBird API token"
-id = "messagebird-api-token"
-regex = '''(?i)(?:messagebird|message-bird|message_bird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{25})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "messagebird","message-bird","message_bird",
-]
-
-[[rules]]
-description = "MessageBird client ID"
-id = "messagebird-client-id"
-regex = '''(?i)(?:messagebird|message-bird|message_bird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "messagebird","message-bird","message_bird",
-]
-
-[[rules]]
-description = "Netlify Access Token"
-id = "netlify-access-token"
-regex = '''(?i)(?:netlify)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{40,46})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "netlify",
-]
-
-[[rules]]
-description = "New Relic user API Key"
-id = "new-relic-user-api-key"
-regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(NRAK-[a-z0-9]{27})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "nrak",
-]
-
-[[rules]]
-description = "New Relic user API ID"
-id = "new-relic-user-api-id"
-regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "new-relic","newrelic","new_relic",
-]
-
-[[rules]]
-description = "New Relic ingest browser API token"
-id = "new-relic-browser-api-token"
-regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(NRJS-[a-f0-9]{19})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "nrjs-",
-]
-
-[[rules]]
-description = "npm access token"
-id = "npm-access-token"
-regex = '''(?i)\b(npm_[a-z0-9]{36})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "npm_",
-]
-
-[[rules]]
-description = "Nytimes Access Token"
-id = "nytimes-access-token"
-regex = '''(?i)(?:nytimes|new-york-times,|newyorktimes)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "nytimes","new-york-times","newyorktimes",
-]
-
-[[rules]]
-description = "Okta Access Token"
-id = "okta-access-token"
-regex = '''(?i)(?:okta)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{42})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "okta",
-]
-
-[[rules]]
-description = "Plaid Client ID"
-id = "plaid-client-id"
-regex = '''(?i)(?:plaid)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{24})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "plaid",
-]
-
-[[rules]]
-description = "Plaid Secret key"
-id = "plaid-secret-key"
-regex = '''(?i)(?:plaid)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{30})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "plaid",
-]
-
-[[rules]]
-description = "Plaid API Token"
-id = "plaid-api-token"
-regex = '''(?i)(?:plaid)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(access-(?:sandbox|development|production)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "plaid",
-]
-
-[[rules]]
-description = "PlanetScale password"
-id = "planetscale-password"
-regex = '''(?i)\b(pscale_pw_(?i)[a-z0-9=\-_\.]{32,64})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "pscale_pw_",
-]
-
-[[rules]]
-description = "PlanetScale API token"
-id = "planetscale-api-token"
-regex = '''(?i)\b(pscale_tkn_(?i)[a-z0-9=\-_\.]{32,64})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "pscale_tkn_",
-]
-
-[[rules]]
-description = "PlanetScale OAuth token"
-id = "planetscale-oauth-token"
-regex = '''(?i)\b(pscale_oauth_(?i)[a-z0-9=\-_\.]{32,64})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "pscale_oauth_",
-]
-
-[[rules]]
-description = "Postman API token"
-id = "postman-api-token"
-regex = '''(?i)\b(PMAK-(?i)[a-f0-9]{24}\-[a-f0-9]{34})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "pmak-",
-]
-
-[[rules]]
-description = "Private Key"
-id = "private-key"
-regex = '''(?i)-----BEGIN[ A-Z0-9_-]{0,100}PRIVATE KEY-----[\s\S-]*KEY----'''
-keywords = [
-    "-----begin",
-]
-
-[[rules]]
-description = "Pulumi API token"
-id = "pulumi-api-token"
-regex = '''(?i)\b(pul-[a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "pul-",
-]
-
-[[rules]]
-description = "PyPI upload token"
-id = "pypi-upload-token"
-regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9\-_]{50,1000}'''
-keywords = [
-    "pypi-ageichlwas5vcmc",
-]
-
-[[rules]]
-description = "Rubygem API token"
-id = "rubygems-api-token"
-regex = '''(?i)\b(rubygems_[a-f0-9]{48})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "rubygems_",
-]
-
-[[rules]]
-description = "RapidAPI Access Token"
-id = "rapidapi-access-token"
-regex = '''(?i)(?:rapidapi)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9_-]{50})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "rapidapi",
-]
-
-[[rules]]
-description = "Sendbird Access ID"
-id = "sendbird-access-id"
-regex = '''(?i)(?:sendbird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "sendbird",
-]
-
-[[rules]]
-description = "Sendbird Access Token"
-id = "sendbird-access-token"
-regex = '''(?i)(?:sendbird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "sendbird",
-]
-
-[[rules]]
-description = "SendGrid API token"
-id = "sendgrid-api-token"
-regex = '''(?i)\b(SG\.(?i)[a-z0-9=_\-\.]{66})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "sg.",
-]
-
-[[rules]]
-description = "Sendinblue API token"
-id = "sendinblue-api-token"
-regex = '''(?i)\b(xkeysib-[a-f0-9]{64}\-(?i)[a-z0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "xkeysib-",
-]
-
-[[rules]]
-description = "Sentry Access Token"
-id = "sentry-access-token"
-regex = '''(?i)(?:sentry)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "sentry",
-]
-
-[[rules]]
-description = "Shippo API token"
-id = "shippo-api-token"
-regex = '''(?i)\b(shippo_(live|test)_[a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "shippo_",
-]
-
-[[rules]]
-description = "Shopify access token"
-id = "shopify-access-token"
-regex = '''shpat_[a-fA-F0-9]{32}'''
-keywords = [
-    "shpat_",
-]
-
-[[rules]]
-description = "Shopify custom access token"
-id = "shopify-custom-access-token"
-regex = '''shpca_[a-fA-F0-9]{32}'''
-keywords = [
-    "shpca_",
-]
-
-[[rules]]
-description = "Shopify private app access token"
-id = "shopify-private-app-access-token"
-regex = '''shppa_[a-fA-F0-9]{32}'''
-keywords = [
-    "shppa_",
-]
-
-[[rules]]
-description = "Shopify shared secret"
-id = "shopify-shared-secret"
-regex = '''shpss_[a-fA-F0-9]{32}'''
-keywords = [
-    "shpss_",
-]
-
-[[rules]]
-description = "Slack token"
-id = "slack-access-token"
-regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})'''
-keywords = [
-    "xoxb","xoxa","xoxp","xoxr","xoxs",
-]
-
-[[rules]]
-description = "Slack Webhook"
-id = "slack-web-hook"
-regex = '''https:\/\/hooks.slack.com\/services\/[A-Za-z0-9+\/]{44,46}'''
-keywords = [
-    "hooks.slack.com",
-]
-
-[[rules]]
-description = "Stripe"
-id = "stripe-access-token"
-regex = '''(?i)(sk|pk)_(test|live)_[0-9a-z]{10,32}'''
-keywords = [
-    "sk_test","pk_test","sk_live","pk_live",
-]
-
-[[rules]]
-description = "Square Access Token"
-id = "square-access-token"
-regex = '''(?i)\b(sq0atp-[0-9A-Za-z\-_]{22})(?:['|\"|\n|\r|\s|\x60]|$)'''
-keywords = [
-    
-]
-
-[[rules]]
-description = "Squarespace Access Token"
-id = "squarespace-access-token"
-regex = '''(?i)(?:squarespace)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "squarespace",
-]
-
-[[rules]]
-description = "SumoLogic Access ID"
-id = "sumologic-access-id"
-regex = '''(?i)(?:sumo)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{14})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "sumo",
-]
-
-[[rules]]
-description = "SumoLogic Access Token"
-id = "sumologic-access-token"
-regex = '''(?i)(?:sumo)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "sumo",
-]
-
-[[rules]]
-description = "Travis CI Access Token"
-id = "travisci-access-token"
-regex = '''(?i)(?:travis)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{22})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "travis",
-]
-
-[[rules]]
-description = "Twilio API Key"
-id = "twilio-api-key"
-regex = '''SK[0-9a-fA-F]{32}'''
-keywords = [
-    "twilio",
-]
-
-[[rules]]
-description = "Twitch API token"
-id = "twitch-api-token"
-regex = '''(?i)(?:twitch)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{30})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "twitch",
-]
-
-[[rules]]
-description = "Twitter API Key"
-id = "twitter-api-key"
-regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{25})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "twitter",
-]
-
-[[rules]]
-description = "Twitter API Secret"
-id = "twitter-api-secret"
-regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{50})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "twitter",
-]
-
-[[rules]]
-description = "Twitter Access Token"
-id = "twitter-access-token"
-regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9]{15,25}-[a-zA-Z0-9]{20,40})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "twitter",
-]
-
-[[rules]]
-description = "Twitter Access Secret"
-id = "twitter-access-secret"
-regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{45})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "twitter",
-]
-
-[[rules]]
-description = "Twitter Bearer Token"
-id = "twitter-bearer-token"
-regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(A{22}[a-zA-Z0-9%]{80,100})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "twitter",
-]
-
-[[rules]]
-description = "Typeform API token"
-id = "typeform-api-token"
-regex = '''(?i)(?:typeform)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(tfp_[a-z0-9\-_\.=]{59})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "tfp_",
-]
-
-[[rules]]
-description = "Yandex API Key"
-id = "yandex-api-key"
-regex = '''(?i)(?:yandex)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(AQVN[A-Za-z0-9_\-]{35,38})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "yandex",
-]
-
-[[rules]]
-description = "Yandex AWS Access Token"
-id = "yandex-aws-access-token"
-regex = '''(?i)(?:yandex)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(YC[a-zA-Z0-9_\-]{38})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "yandex",
-]
-
-[[rules]]
-description = "Yandex Access Token"
-id = "yandex-access-token"
-regex = '''(?i)(?:yandex)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(t1\.[A-Z0-9a-z_-]+[=]{0,2}\.[A-Z0-9a-z_-]{86}[=]{0,2})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "yandex",
-]
-
-[[rules]]
-description = "Zendesk Secret Key"
-id = "zendesk-secret-key"
-regex = '''(?i)(?:zendesk)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-keywords = [
-    "zendesk",
-]
-
-[[rules]]
-description = "Generic API Key"
-id = "generic-api-key"
-regex = '''(?i)(?:key|api|token|secret|client|passwd|password|auth|access)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-z\-_.=]{10,150})(?:['|\"|\n|\r|\s|\x60]|$)'''
-secretGroup = 1
-entropy = 3.5
-keywords = [
-    "key","api","token","secret","client","passwd","password","auth","access",
+    "key","api","token","secret","client","passwd","password","auth","access",
 ]
 ]
 [rules.allowlist]
 [rules.allowlist]
 stopwords= [
 stopwords= [
@@ -2462,163 +1778,847 @@ stopwords= [
     "stat",
     "stat",
     "state",
     "state",
     "twitter",
     "twitter",
-    "utility",
-    "converter",
-    "hosting",
-    "devise",
-    "liferay",
-    "updated",
-    "force",
-    "tip_",
-    "tip-",
-    "tip.",
-    "behavior",
-    "active",
-    "call",
-    "answer",
-    "deck",
-    "better",
-    "principle",
-    "ches",
-    "bar_",
-    "bar-",
-    "bar.",
-    "reddit",
-    "three",
-    "haxe",
-    "just",
-    "plug-in",
-    "agile",
-    "manual",
-    "tetri",
-    "super",
-    "beta",
-    "parsing",
-    "doctrine",
-    "minecraft",
-    "useful",
-    "perl",
-    "sharing",
-    "agent",
-    "switch",
-    "view",
-    "dash",
-    "channel",
-    "repo",
-    "pebble",
-    "profiler",
-    "warning",
-    "cluster",
-    "running",
-    "markup",
-    "evented",
-    "mod_",
-    "mod-",
-    "mod.",
-    "share",
-    "csv_",
-    "csv-",
-    "csv.",
-    "response",
-    "good",
-    "house",
-    "connect",
-    "built",
-    "build",
-    "find",
-    "ipython",
-    "webgl",
-    "big_",
-    "big-",
-    "big.",
-    "google",
-    "scala",
-    "sdl_",
-    "sdl-",
-    "sdl.",
-    "sdk_",
-    "sdk-",
-    "sdk.",
-    "native",
-    "day_",
-    "day-",
-    "day.",
-    "puppet",
-    "text",
-    "routing",
-    "helper",
-    "linkedin",
-    "crawler",
-    "host",
-    "guard",
-    "merchant",
-    "poker",
-    "over",
-    "writing",
-    "free",
-    "classe",
-    "component",
-    "craft",
-    "nodej",
-    "phoenix",
-    "longer",
-    "quick",
-    "lazy",
-    "memory",
-    "clone",
-    "hacker",
-    "middleman",
-    "factory",
-    "motion",
-    "multiple",
-    "tornado",
-    "hack",
-    "ssh_",
-    "ssh-",
-    "ssh.",
-    "review",
-    "vimrc",
-    "driver",
-    "driven",
-    "blog",
-    "particle",
-    "table",
-    "intro",
-    "importer",
-    "thrift",
-    "xmpp",
-    "framework",
-    "refresh",
-    "react",
-    "font",
-    "librarie",
-    "variou",
-    "formatter",
-    "analysi",
-    "karma",
-    "scroll",
-    "tut_",
-    "tut-",
-    "tut.",
-    "apple",
-    "tag_",
-    "tag-",
-    "tag.",
-    "tab_",
-    "tab-",
-    "tab.",
-    "category",
-    "ionic",
-    "cache",
-    "homebrew",
-    "reverse",
-    "english",
-    "getting",
-    "shipping",
-    "clojure",
-    "boot",
-    "book",
-    "branch",
-    "combination",
-    "combo",
+    "utility",
+    "converter",
+    "hosting",
+    "devise",
+    "liferay",
+    "updated",
+    "force",
+    "tip_",
+    "tip-",
+    "tip.",
+    "behavior",
+    "active",
+    "call",
+    "answer",
+    "deck",
+    "better",
+    "principle",
+    "ches",
+    "bar_",
+    "bar-",
+    "bar.",
+    "reddit",
+    "three",
+    "haxe",
+    "just",
+    "plug-in",
+    "agile",
+    "manual",
+    "tetri",
+    "super",
+    "beta",
+    "parsing",
+    "doctrine",
+    "minecraft",
+    "useful",
+    "perl",
+    "sharing",
+    "agent",
+    "switch",
+    "view",
+    "dash",
+    "channel",
+    "repo",
+    "pebble",
+    "profiler",
+    "warning",
+    "cluster",
+    "running",
+    "markup",
+    "evented",
+    "mod_",
+    "mod-",
+    "mod.",
+    "share",
+    "csv_",
+    "csv-",
+    "csv.",
+    "response",
+    "good",
+    "house",
+    "connect",
+    "built",
+    "build",
+    "find",
+    "ipython",
+    "webgl",
+    "big_",
+    "big-",
+    "big.",
+    "google",
+    "scala",
+    "sdl_",
+    "sdl-",
+    "sdl.",
+    "sdk_",
+    "sdk-",
+    "sdk.",
+    "native",
+    "day_",
+    "day-",
+    "day.",
+    "puppet",
+    "text",
+    "routing",
+    "helper",
+    "linkedin",
+    "crawler",
+    "host",
+    "guard",
+    "merchant",
+    "poker",
+    "over",
+    "writing",
+    "free",
+    "classe",
+    "component",
+    "craft",
+    "nodej",
+    "phoenix",
+    "longer",
+    "quick",
+    "lazy",
+    "memory",
+    "clone",
+    "hacker",
+    "middleman",
+    "factory",
+    "motion",
+    "multiple",
+    "tornado",
+    "hack",
+    "ssh_",
+    "ssh-",
+    "ssh.",
+    "review",
+    "vimrc",
+    "driver",
+    "driven",
+    "blog",
+    "particle",
+    "table",
+    "intro",
+    "importer",
+    "thrift",
+    "xmpp",
+    "framework",
+    "refresh",
+    "react",
+    "font",
+    "librarie",
+    "variou",
+    "formatter",
+    "analysi",
+    "karma",
+    "scroll",
+    "tut_",
+    "tut-",
+    "tut.",
+    "apple",
+    "tag_",
+    "tag-",
+    "tag.",
+    "tab_",
+    "tab-",
+    "tab.",
+    "category",
+    "ionic",
+    "cache",
+    "homebrew",
+    "reverse",
+    "english",
+    "getting",
+    "shipping",
+    "clojure",
+    "boot",
+    "book",
+    "branch",
+    "combination",
+    "combo",
+]
+[[rules]]
+description = "GitHub App Token"
+id = "github-app-token"
+regex = '''(ghu|ghs)_[0-9a-zA-Z]{36}'''
+keywords = [
+    "ghu_","ghs_",
+]
+
+[[rules]]
+description = "GitHub OAuth Access Token"
+id = "github-oauth"
+regex = '''gho_[0-9a-zA-Z]{36}'''
+keywords = [
+    "gho_",
+]
+
+[[rules]]
+description = "GitHub Personal Access Token"
+id = "github-pat"
+regex = '''ghp_[0-9a-zA-Z]{36}'''
+keywords = [
+    "ghp_",
+]
+
+[[rules]]
+description = "GitHub Refresh Token"
+id = "github-refresh-token"
+regex = '''ghr_[0-9a-zA-Z]{36}'''
+keywords = [
+    "ghr_",
+]
+
+[[rules]]
+description = "GitLab Personal Access Token"
+id = "gitlab-pat"
+regex = '''glpat-[0-9a-zA-Z\-\_]{20}'''
+keywords = [
+    "glpat-",
+]
+
+[[rules]]
+description = "Gitter Access Token"
+id = "gitter-access-token"
+regex = '''(?i)(?:gitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9_-]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "gitter",
+]
+
+[[rules]]
+description = "GoCardless API token"
+id = "gocardless-api-token"
+regex = '''(?i)(?:gocardless)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(live_(?i)[a-z0-9\-_=]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "live_","gocardless",
+]
+
+[[rules]]
+description = "HashiCorp Terraform user/org API token"
+id = "hashicorp-tf-api-token"
+regex = '''(?i)[a-z0-9]{14}\.atlasv1\.[a-z0-9\-_=]{60,70}'''
+keywords = [
+    "atlasv1",
+]
+
+[[rules]]
+description = "Heroku API Key"
+id = "heroku-api-key"
+regex = '''(?i)(?:heroku)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "heroku",
+]
+
+[[rules]]
+description = "HubSpot API Token"
+id = "hubspot-api-key"
+regex = '''(?i)(?:hubspot)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "hubspot",
+]
+
+[[rules]]
+description = "Intercom API Token"
+id = "intercom-api-key"
+regex = '''(?i)(?:intercom)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{60})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "intercom",
+]
+
+[[rules]]
+description = "Kraken Access Token"
+id = "kraken-access-token"
+regex = '''(?i)(?:kraken)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9\/=_\+\-]{80,90})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "kraken",
+]
+
+[[rules]]
+description = "Kucoin Access Token"
+id = "kucoin-access-token"
+regex = '''(?i)(?:kucoin)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{24})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "kucoin",
+]
+
+[[rules]]
+description = "Kucoin Secret Key"
+id = "kucoin-secret-key"
+regex = '''(?i)(?:kucoin)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "kucoin",
+]
+
+[[rules]]
+description = "Launchdarkly Access Token"
+id = "launchdarkly-access-token"
+regex = '''(?i)(?:launchdarkly)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "launchdarkly",
+]
+
+[[rules]]
+description = "Linear API Token"
+id = "linear-api-key"
+regex = '''lin_api_(?i)[a-z0-9]{40}'''
+keywords = [
+    "lin_api_",
+]
+
+[[rules]]
+description = "Linear Client Secret"
+id = "linear-client-secret"
+regex = '''(?i)(?:linear)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "linear",
+]
+
+[[rules]]
+description = "LinkedIn Client ID"
+id = "linkedin-client-id"
+regex = '''(?i)(?:linkedin|linked-in)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{14})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "linkedin","linked-in",
+]
+
+[[rules]]
+description = "LinkedIn Client secret"
+id = "linkedin-client-secret"
+regex = '''(?i)(?:linkedin|linked-in)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "linkedin","linked-in",
+]
+
+[[rules]]
+description = "Lob API Key"
+id = "lob-api-key"
+regex = '''(?i)(?:lob)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}((live|test)_[a-f0-9]{35})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "test_","live_",
+]
+
+[[rules]]
+description = "Lob Publishable API Key"
+id = "lob-pub-api-key"
+regex = '''(?i)(?:lob)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}((test|live)_pub_[a-f0-9]{31})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "test_pub","live_pub","_pub",
+]
+
+[[rules]]
+description = "Mailchimp API key"
+id = "mailchimp-api-key"
+regex = '''(?i)(?:mailchimp)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32}-us20)(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "mailchimp",
+]
+
+[[rules]]
+description = "Mailgun private API token"
+id = "mailgun-private-api-token"
+regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(key-[a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "mailgun",
+]
+
+[[rules]]
+description = "Mailgun public validation key"
+id = "mailgun-pub-key"
+regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(pubkey-[a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "mailgun",
+]
+
+[[rules]]
+description = "Mailgun webhook signing key"
+id = "mailgun-signing-key"
+regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-h0-9]{32}-[a-h0-9]{8}-[a-h0-9]{8})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "mailgun",
+]
+
+[[rules]]
+description = "MapBox API token"
+id = "mapbox-api-token"
+regex = '''(?i)(?:mapbox)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(pk\.[a-z0-9]{60}\.[a-z0-9]{22})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "mapbox",
+]
+
+[[rules]]
+description = "Mattermost Access Token"
+id = "mattermost-access-token"
+regex = '''(?i)(?:mattermost)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{26})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "mattermost",
+]
+
+[[rules]]
+description = "MessageBird API token"
+id = "messagebird-api-token"
+regex = '''(?i)(?:messagebird|message-bird|message_bird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{25})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "messagebird","message-bird","message_bird",
+]
+
+[[rules]]
+description = "MessageBird client ID"
+id = "messagebird-client-id"
+regex = '''(?i)(?:messagebird|message-bird|message_bird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "messagebird","message-bird","message_bird",
+]
+
+[[rules]]
+description = "Netlify Access Token"
+id = "netlify-access-token"
+regex = '''(?i)(?:netlify)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{40,46})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "netlify",
+]
+
+[[rules]]
+description = "New Relic ingest browser API token"
+id = "new-relic-browser-api-token"
+regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(NRJS-[a-f0-9]{19})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "nrjs-",
+]
+
+[[rules]]
+description = "New Relic user API ID"
+id = "new-relic-user-api-id"
+regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "new-relic","newrelic","new_relic",
+]
+
+[[rules]]
+description = "New Relic user API Key"
+id = "new-relic-user-api-key"
+regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(NRAK-[a-z0-9]{27})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "nrak",
+]
+
+[[rules]]
+description = "npm access token"
+id = "npm-access-token"
+regex = '''(?i)\b(npm_[a-z0-9]{36})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "npm_",
+]
+
+[[rules]]
+description = "Nytimes Access Token"
+id = "nytimes-access-token"
+regex = '''(?i)(?:nytimes|new-york-times,|newyorktimes)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{32})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "nytimes","new-york-times","newyorktimes",
+]
+
+[[rules]]
+description = "Okta Access Token"
+id = "okta-access-token"
+regex = '''(?i)(?:okta)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{42})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "okta",
+]
+
+[[rules]]
+description = "Plaid API Token"
+id = "plaid-api-token"
+regex = '''(?i)(?:plaid)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(access-(?:sandbox|development|production)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "plaid",
+]
+
+[[rules]]
+description = "Plaid Client ID"
+id = "plaid-client-id"
+regex = '''(?i)(?:plaid)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{24})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "plaid",
+]
+
+[[rules]]
+description = "Plaid Secret key"
+id = "plaid-secret-key"
+regex = '''(?i)(?:plaid)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{30})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "plaid",
+]
+
+[[rules]]
+description = "PlanetScale API token"
+id = "planetscale-api-token"
+regex = '''(?i)\b(pscale_tkn_(?i)[a-z0-9=\-_\.]{32,64})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "pscale_tkn_",
+]
+
+[[rules]]
+description = "PlanetScale OAuth token"
+id = "planetscale-oauth-token"
+regex = '''(?i)\b(pscale_oauth_(?i)[a-z0-9=\-_\.]{32,64})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "pscale_oauth_",
+]
+
+[[rules]]
+description = "PlanetScale password"
+id = "planetscale-password"
+regex = '''(?i)\b(pscale_pw_(?i)[a-z0-9=\-_\.]{32,64})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "pscale_pw_",
+]
+
+[[rules]]
+description = "Postman API token"
+id = "postman-api-token"
+regex = '''(?i)\b(PMAK-(?i)[a-f0-9]{24}\-[a-f0-9]{34})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "pmak-",
+]
+
+[[rules]]
+description = "Private Key"
+id = "private-key"
+regex = '''(?i)-----BEGIN[ A-Z0-9_-]{0,100}PRIVATE KEY-----[\s\S-]*KEY----'''
+keywords = [
+    "-----begin",
+]
+
+[[rules]]
+description = "Pulumi API token"
+id = "pulumi-api-token"
+regex = '''(?i)\b(pul-[a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "pul-",
+]
+
+[[rules]]
+description = "PyPI upload token"
+id = "pypi-upload-token"
+regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9\-_]{50,1000}'''
+keywords = [
+    "pypi-ageichlwas5vcmc",
+]
+
+[[rules]]
+description = "RapidAPI Access Token"
+id = "rapidapi-access-token"
+regex = '''(?i)(?:rapidapi)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9_-]{50})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "rapidapi",
+]
+
+[[rules]]
+description = "Rubygem API token"
+id = "rubygems-api-token"
+regex = '''(?i)\b(rubygems_[a-f0-9]{48})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "rubygems_",
+]
+
+[[rules]]
+description = "Sendbird Access ID"
+id = "sendbird-access-id"
+regex = '''(?i)(?:sendbird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "sendbird",
+]
+
+[[rules]]
+description = "Sendbird Access Token"
+id = "sendbird-access-token"
+regex = '''(?i)(?:sendbird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "sendbird",
+]
+
+[[rules]]
+description = "SendGrid API token"
+id = "sendgrid-api-token"
+regex = '''(?i)\b(SG\.(?i)[a-z0-9=_\-\.]{66})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "sg.",
+]
+
+[[rules]]
+description = "Sendinblue API token"
+id = "sendinblue-api-token"
+regex = '''(?i)\b(xkeysib-[a-f0-9]{64}\-(?i)[a-z0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "xkeysib-",
+]
+
+[[rules]]
+description = "Sentry Access Token"
+id = "sentry-access-token"
+regex = '''(?i)(?:sentry)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "sentry",
+]
+
+[[rules]]
+description = "Shippo API token"
+id = "shippo-api-token"
+regex = '''(?i)\b(shippo_(live|test)_[a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "shippo_",
+]
+
+[[rules]]
+description = "Shopify access token"
+id = "shopify-access-token"
+regex = '''shpat_[a-fA-F0-9]{32}'''
+keywords = [
+    "shpat_",
+]
+
+[[rules]]
+description = "Shopify custom access token"
+id = "shopify-custom-access-token"
+regex = '''shpca_[a-fA-F0-9]{32}'''
+keywords = [
+    "shpca_",
+]
+
+[[rules]]
+description = "Shopify private app access token"
+id = "shopify-private-app-access-token"
+regex = '''shppa_[a-fA-F0-9]{32}'''
+keywords = [
+    "shppa_",
+]
+
+[[rules]]
+description = "Shopify shared secret"
+id = "shopify-shared-secret"
+regex = '''shpss_[a-fA-F0-9]{32}'''
+keywords = [
+    "shpss_",
+]
+
+[[rules]]
+description = "Slack token"
+id = "slack-access-token"
+regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})'''
+keywords = [
+    "xoxb","xoxa","xoxp","xoxr","xoxs",
+]
+
+[[rules]]
+description = "Slack Webhook"
+id = "slack-web-hook"
+regex = '''https:\/\/hooks.slack.com\/services\/[A-Za-z0-9+\/]{44,46}'''
+keywords = [
+    "hooks.slack.com",
+]
+
+[[rules]]
+description = "Square Access Token"
+id = "square-access-token"
+regex = '''(?i)\b(sq0atp-[0-9A-Za-z\-_]{22})(?:['|\"|\n|\r|\s|\x60]|$)'''
+keywords = [
+    
+]
+
+[[rules]]
+description = "Squarespace Access Token"
+id = "squarespace-access-token"
+regex = '''(?i)(?:squarespace)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "squarespace",
+]
+
+[[rules]]
+description = "Stripe"
+id = "stripe-access-token"
+regex = '''(?i)(sk|pk)_(test|live)_[0-9a-z]{10,32}'''
+keywords = [
+    "sk_test","pk_test","sk_live","pk_live",
+]
+
+[[rules]]
+description = "SumoLogic Access ID"
+id = "sumologic-access-id"
+regex = '''(?i)(?:sumo)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{14})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "sumo",
+]
+
+[[rules]]
+description = "SumoLogic Access Token"
+id = "sumologic-access-token"
+regex = '''(?i)(?:sumo)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "sumo",
+]
+
+[[rules]]
+description = "Travis CI Access Token"
+id = "travisci-access-token"
+regex = '''(?i)(?:travis)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{22})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "travis",
+]
+
+[[rules]]
+description = "Twilio API Key"
+id = "twilio-api-key"
+regex = '''SK[0-9a-fA-F]{32}'''
+keywords = [
+    "twilio",
+]
+
+[[rules]]
+description = "Twitch API token"
+id = "twitch-api-token"
+regex = '''(?i)(?:twitch)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{30})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "twitch",
+]
+
+[[rules]]
+description = "Twitter Access Secret"
+id = "twitter-access-secret"
+regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{45})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "twitter",
+]
+
+[[rules]]
+description = "Twitter Access Token"
+id = "twitter-access-token"
+regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9]{15,25}-[a-zA-Z0-9]{20,40})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "twitter",
+]
+
+[[rules]]
+description = "Twitter API Key"
+id = "twitter-api-key"
+regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{25})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "twitter",
 ]
 ]
+
+[[rules]]
+description = "Twitter API Secret"
+id = "twitter-api-secret"
+regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{50})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "twitter",
+]
+
+[[rules]]
+description = "Twitter Bearer Token"
+id = "twitter-bearer-token"
+regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(A{22}[a-zA-Z0-9%]{80,100})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "twitter",
+]
+
+[[rules]]
+description = "Typeform API token"
+id = "typeform-api-token"
+regex = '''(?i)(?:typeform)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(tfp_[a-z0-9\-_\.=]{59})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "tfp_",
+]
+
+[[rules]]
+description = "Yandex Access Token"
+id = "yandex-access-token"
+regex = '''(?i)(?:yandex)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(t1\.[A-Z0-9a-z_-]+[=]{0,2}\.[A-Z0-9a-z_-]{86}[=]{0,2})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "yandex",
+]
+
+[[rules]]
+description = "Yandex API Key"
+id = "yandex-api-key"
+regex = '''(?i)(?:yandex)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(AQVN[A-Za-z0-9_\-]{35,38})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "yandex",
+]
+
+[[rules]]
+description = "Yandex AWS Access Token"
+id = "yandex-aws-access-token"
+regex = '''(?i)(?:yandex)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(YC[a-zA-Z0-9_\-]{38})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "yandex",
+]
+
+[[rules]]
+description = "Zendesk Secret Key"
+id = "zendesk-secret-key"
+regex = '''(?i)(?:zendesk)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)'''
+secretGroup = 1
+keywords = [
+    "zendesk",
+]
+

+ 1 - 1
detect/detect.go

@@ -136,7 +136,7 @@ func (d *Detector) DetectString(content string) []report.Finding {
 }
 }
 
 
 // detectRule scans the given fragment for the given rule and returns a list of findings
 // detectRule scans the given fragment for the given rule and returns a list of findings
-func (d *Detector) detectRule(fragment Fragment, rule *config.Rule) []report.Finding {
+func (d *Detector) detectRule(fragment Fragment, rule config.Rule) []report.Finding {
 	var findings []report.Finding
 	var findings []report.Finding
 
 
 	// check if filepath or commit is allowed for this rule
 	// check if filepath or commit is allowed for this rule

+ 1 - 1
detect/utils.go

@@ -72,7 +72,7 @@ func filter(findings []report.Finding, redact bool) []report.Finding {
 
 
 					genericMatch := strings.Replace(f.Match, f.Secret, "REDACTED", -1)
 					genericMatch := strings.Replace(f.Match, f.Secret, "REDACTED", -1)
 					betterMatch := strings.Replace(fPrime.Match, fPrime.Secret, "REDACTED", -1)
 					betterMatch := strings.Replace(fPrime.Match, fPrime.Secret, "REDACTED", -1)
-					log.Debug().Msgf("skipping %s finding (%s), %s rule takes precendence (%s)", f.RuleID, genericMatch, fPrime.RuleID, betterMatch)
+					log.Trace().Msgf("skipping %s finding (%s), %s rule takes precendence (%s)", f.RuleID, genericMatch, fPrime.RuleID, betterMatch)
 					include = false
 					include = false
 					break
 					break
 				}
 				}

+ 1 - 1
report/sarif.go

@@ -53,7 +53,7 @@ func hasEmptyRules(tool Tool) bool {
 func getRules(cfg config.Config) []Rules {
 func getRules(cfg config.Config) []Rules {
 	// TODO	for _, rule := range cfg.Rules {
 	// TODO	for _, rule := range cfg.Rules {
 	var rules []Rules
 	var rules []Rules
-	for _, rule := range cfg.Rules {
+	for _, rule := range cfg.OrderedRules() {
 		shortDescription := ShortDescription{
 		shortDescription := ShortDescription{
 			Text: rule.Description,
 			Text: rule.Description,
 		}
 		}

+ 3 - 1
report/sarif_test.go

@@ -1,6 +1,7 @@
 package report
 package report
 
 
 import (
 import (
+	"fmt"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
@@ -27,7 +28,7 @@ func TestWriteSarif(t *testing.T) {
 			findings: []Finding{
 			findings: []Finding{
 				{
 				{
 
 
-					Description: "",
+					Description: "A test rule",
 					RuleID:      "test-rule",
 					RuleID:      "test-rule",
 					Match:       "line containing secret",
 					Match:       "line containing secret",
 					Secret:      "a secret",
 					Secret:      "a secret",
@@ -73,6 +74,7 @@ func TestWriteSarif(t *testing.T) {
 			t.Error(err)
 			t.Error(err)
 		}
 		}
 		err = writeSarif(cfg, test.findings, tmpfile)
 		err = writeSarif(cfg, test.findings, tmpfile)
+		fmt.Println(cfg)
 		if err != nil {
 		if err != nil {
 			os.Remove(tmpfile.Name())
 			os.Remove(tmpfile.Name())
 			t.Error(err)
 			t.Error(err)

+ 10 - 0
testdata/config/base.toml

@@ -0,0 +1,10 @@
+title = "gitleaks config"
+
+[extend]
+path="../testdata/config/extend_1.toml"
+
+[[rules]]
+    description = "AWS Secret Key"
+    id = "aws-secret-key"
+    regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}'''
+    tags = ["key", "AWS"]

+ 10 - 0
testdata/config/extend_1.toml

@@ -0,0 +1,10 @@
+title = "gitleaks extended 1"
+
+[extend]
+path="../testdata/config/extend_2.toml"
+
+[[rules]]
+    description = "AWS Access Key"
+    id = "aws-access-key"
+    regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}'''
+    tags = ["key", "AWS"]

+ 10 - 0
testdata/config/extend_2.toml

@@ -0,0 +1,10 @@
+title = "gitleaks extended 2"
+
+[extend]
+path="../testdata/config/extend_3.toml"
+
+[[rules]]
+    description = "AWS Secret Key"
+    id = "aws-secret-key-again"
+    regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}'''
+    tags = ["key", "AWS"]

+ 9 - 0
testdata/config/extend_3.toml

@@ -0,0 +1,9 @@
+title = "gitleaks extended 3"
+
+## This should not be loaded since we can only extend configs to a depth of 3
+
+[[rules]]
+    description = "AWS Secret Key"
+    id = "aws-secret-key-again-again"
+    regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}'''
+    tags = ["key", "AWS"]

+ 33 - 0
testdata/config/simple.toml

@@ -9,162 +9,195 @@ title = "gitleaks config"
 
 
 [[rules]]
 [[rules]]
     description = "AWS Secret Key"
     description = "AWS Secret Key"
+    id = "aws-secret-key"
     regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}'''
     regex = '''(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}'''
     tags = ["key", "AWS"]
     tags = ["key", "AWS"]
 
 
 [[rules]]
 [[rules]]
     description = "AWS MWS key"
     description = "AWS MWS key"
+    id = "aws-mws-key"
     regex = '''amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'''
     regex = '''amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'''
     tags = ["key", "AWS", "MWS"]
     tags = ["key", "AWS", "MWS"]
 
 
 [[rules]]
 [[rules]]
     description = "Facebook Secret Key"
     description = "Facebook Secret Key"
+    id = "facebook-secret-key"
     regex = '''(?i)(facebook|fb)(.{0,20})?(?-i)['\"][0-9a-f]{32}['\"]'''
     regex = '''(?i)(facebook|fb)(.{0,20})?(?-i)['\"][0-9a-f]{32}['\"]'''
     tags = ["key", "Facebook"]
     tags = ["key", "Facebook"]
 
 
 [[rules]]
 [[rules]]
     description = "Facebook Client ID"
     description = "Facebook Client ID"
+    id = "facebook-client-id"
     regex = '''(?i)(facebook|fb)(.{0,20})?['\"][0-9]{13,17}['\"]'''
     regex = '''(?i)(facebook|fb)(.{0,20})?['\"][0-9]{13,17}['\"]'''
     tags = ["key", "Facebook"]
     tags = ["key", "Facebook"]
 
 
 [[rules]]
 [[rules]]
     description = "Twitter Secret Key"
     description = "Twitter Secret Key"
+    id = "twitter-secret-key"
     regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{35,44}['\"]'''
     regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{35,44}['\"]'''
     tags = ["key", "Twitter"]
     tags = ["key", "Twitter"]
 
 
 [[rules]]
 [[rules]]
     description = "Twitter Client ID"
     description = "Twitter Client ID"
+    id = "twitter-client-id"
     regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{18,25}['\"]'''
     regex = '''(?i)twitter(.{0,20})?['\"][0-9a-z]{18,25}['\"]'''
     tags = ["client", "Twitter"]
     tags = ["client", "Twitter"]
 
 
 [[rules]]
 [[rules]]
     description = "Github Personal Access Token"
     description = "Github Personal Access Token"
+    id = "github-pat"
     regex = '''ghp_[0-9a-zA-Z]{36}'''
     regex = '''ghp_[0-9a-zA-Z]{36}'''
     tags = ["key", "Github"]
     tags = ["key", "Github"]
 [[rules]]
 [[rules]]
     description = "Github OAuth Access Token"
     description = "Github OAuth Access Token"
+    id = "github-oauth"
     regex = '''gho_[0-9a-zA-Z]{36}'''
     regex = '''gho_[0-9a-zA-Z]{36}'''
     tags = ["key", "Github"]
     tags = ["key", "Github"]
 [[rules]]
 [[rules]]
+    id = "github-app"
     description = "Github App Token"
     description = "Github App Token"
     regex = '''(ghu|ghs)_[0-9a-zA-Z]{36}'''
     regex = '''(ghu|ghs)_[0-9a-zA-Z]{36}'''
     tags = ["key", "Github"]
     tags = ["key", "Github"]
 [[rules]]
 [[rules]]
+    id = "github-refresh"
     description = "Github Refresh Token"
     description = "Github Refresh Token"
     regex = '''ghr_[0-9a-zA-Z]{76}'''
     regex = '''ghr_[0-9a-zA-Z]{76}'''
     tags = ["key", "Github"]
     tags = ["key", "Github"]
 
 
 [[rules]]
 [[rules]]
+    id = "linkedin-client"
     description = "LinkedIn Client ID"
     description = "LinkedIn Client ID"
     regex = '''(?i)linkedin(.{0,20})?(?-i)[0-9a-z]{12}'''
     regex = '''(?i)linkedin(.{0,20})?(?-i)[0-9a-z]{12}'''
     tags = ["client", "LinkedIn"]
     tags = ["client", "LinkedIn"]
 
 
 [[rules]]
 [[rules]]
+    id = "linkedin-secret"
     description = "LinkedIn Secret Key"
     description = "LinkedIn Secret Key"
     regex = '''(?i)linkedin(.{0,20})?[0-9a-z]{16}'''
     regex = '''(?i)linkedin(.{0,20})?[0-9a-z]{16}'''
     tags = ["secret", "LinkedIn"]
     tags = ["secret", "LinkedIn"]
 
 
 [[rules]]
 [[rules]]
+    id = "slack"
     description = "Slack"
     description = "Slack"
     regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})?'''
     regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})?'''
     tags = ["key", "Slack"]
     tags = ["key", "Slack"]
 
 
 [[rules]]
 [[rules]]
+    id = "apkey"
     description = "Asymmetric Private Key"
     description = "Asymmetric Private Key"
     regex = '''-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----'''
     regex = '''-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----'''
     tags = ["key", "AsymmetricPrivateKey"]
     tags = ["key", "AsymmetricPrivateKey"]
 
 
 [[rules]]
 [[rules]]
+    id = "google"
     description = "Google API key"
     description = "Google API key"
     regex = '''AIza[0-9A-Za-z\-_]{35}'''
     regex = '''AIza[0-9A-Za-z\-_]{35}'''
     tags = ["key", "Google"]
     tags = ["key", "Google"]
 
 
 [[rules]]
 [[rules]]
+    id = "google"
     description = "Google (GCP) Service Account"
     description = "Google (GCP) Service Account"
     regex = '''"type": "service_account"'''
     regex = '''"type": "service_account"'''
     tags = ["key", "Google"]
     tags = ["key", "Google"]
 
 
 [[rules]]
 [[rules]]
+    id = "heroku"
     description = "Heroku API key"
     description = "Heroku API key"
     regex = '''(?i)heroku(.{0,20})?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'''
     regex = '''(?i)heroku(.{0,20})?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'''
     tags = ["key", "Heroku"]
     tags = ["key", "Heroku"]
 
 
 [[rules]]
 [[rules]]
+    id = "mailchimp"
     description = "MailChimp API key"
     description = "MailChimp API key"
     regex = '''(?i)(mailchimp|mc)(.{0,20})?[0-9a-f]{32}-us[0-9]{1,2}'''
     regex = '''(?i)(mailchimp|mc)(.{0,20})?[0-9a-f]{32}-us[0-9]{1,2}'''
     tags = ["key", "Mailchimp"]
     tags = ["key", "Mailchimp"]
 
 
 [[rules]]
 [[rules]]
+    id = "mailgun"
     description = "Mailgun API key"
     description = "Mailgun API key"
     regex = '''((?i)(mailgun|mg)(.{0,20})?)?key-[0-9a-z]{32}'''
     regex = '''((?i)(mailgun|mg)(.{0,20})?)?key-[0-9a-z]{32}'''
     tags = ["key", "Mailgun"]
     tags = ["key", "Mailgun"]
 
 
 [[rules]]
 [[rules]]
+    id = "paypal"
     description = "PayPal Braintree access token"
     description = "PayPal Braintree access token"
     regex = '''access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}'''
     regex = '''access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}'''
     tags = ["key", "Paypal"]
     tags = ["key", "Paypal"]
 
 
 [[rules]]
 [[rules]]
+    id = "piacatic"
     description = "Picatic API key"
     description = "Picatic API key"
     regex = '''sk_live_[0-9a-z]{32}'''
     regex = '''sk_live_[0-9a-z]{32}'''
     tags = ["key", "Picatic"]
     tags = ["key", "Picatic"]
 
 
 [[rules]]
 [[rules]]
+    id = "sendgrid"
     description = "SendGrid API Key"
     description = "SendGrid API Key"
     regex = '''SG\.[\w_]{16,32}\.[\w_]{16,64}'''
     regex = '''SG\.[\w_]{16,32}\.[\w_]{16,64}'''
     tags = ["key", "SendGrid"]
     tags = ["key", "SendGrid"]
 
 
 [[rules]]
 [[rules]]
+    id = "slack-webhook"
     description = "Slack Webhook"
     description = "Slack Webhook"
     regex = '''https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8,12}/[a-zA-Z0-9_]{24}'''
     regex = '''https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8,12}/[a-zA-Z0-9_]{24}'''
     tags = ["key", "slack"]
     tags = ["key", "slack"]
 
 
 [[rules]]
 [[rules]]
+    id = "stripe"
     description = "Stripe API key"
     description = "Stripe API key"
     regex = '''(?i)stripe(.{0,20})?[sr]k_live_[0-9a-zA-Z]{24}'''
     regex = '''(?i)stripe(.{0,20})?[sr]k_live_[0-9a-zA-Z]{24}'''
     tags = ["key", "Stripe"]
     tags = ["key", "Stripe"]
 
 
 [[rules]]
 [[rules]]
+    id = "square"
     description = "Square access token"
     description = "Square access token"
     regex = '''sq0atp-[0-9A-Za-z\-_]{22}'''
     regex = '''sq0atp-[0-9A-Za-z\-_]{22}'''
     tags = ["key", "square"]
     tags = ["key", "square"]
 
 
 [[rules]]
 [[rules]]
+    id = "square-oauth"
     description = "Square OAuth secret"
     description = "Square OAuth secret"
     regex = '''sq0csp-[0-9A-Za-z\-_]{43}'''
     regex = '''sq0csp-[0-9A-Za-z\-_]{43}'''
     tags = ["key", "square"]
     tags = ["key", "square"]
 
 
 [[rules]]
 [[rules]]
+    id = "twilio"
     description = "Twilio API key"
     description = "Twilio API key"
     regex = '''(?i)twilio(.{0,20})?SK[0-9a-f]{32}'''
     regex = '''(?i)twilio(.{0,20})?SK[0-9a-f]{32}'''
     tags = ["key", "twilio"]
     tags = ["key", "twilio"]
 
 
 [[rules]]
 [[rules]]
+    id = "dynatrace"
     description = "Dynatrace ttoken"
     description = "Dynatrace ttoken"
     regex = '''dt0[a-zA-Z]{1}[0-9]{2}\.[A-Z0-9]{24}\.[A-Z0-9]{64}'''
     regex = '''dt0[a-zA-Z]{1}[0-9]{2}\.[A-Z0-9]{24}\.[A-Z0-9]{64}'''
     tags = ["key", "Dynatrace"]
     tags = ["key", "Dynatrace"]
 
 
 [[rules]]
 [[rules]]
+    id = "shopify"
     description = "Shopify shared secret"
     description = "Shopify shared secret"
     regex = '''shpss_[a-fA-F0-9]{32}'''
     regex = '''shpss_[a-fA-F0-9]{32}'''
     tags = ["key", "Shopify"]
     tags = ["key", "Shopify"]
 
 
 [[rules]]
 [[rules]]
+    id = "shopify-access"
     description = "Shopify access token"
     description = "Shopify access token"
     regex = '''shpat_[a-fA-F0-9]{32}'''
     regex = '''shpat_[a-fA-F0-9]{32}'''
     tags = ["key", "Shopify"]
     tags = ["key", "Shopify"]
 
 
 [[rules]]
 [[rules]]
+    id = "shopify-custom"
     description = "Shopify custom app access token"
     description = "Shopify custom app access token"
     regex = '''shpca_[a-fA-F0-9]{32}'''
     regex = '''shpca_[a-fA-F0-9]{32}'''
     tags = ["key", "Shopify"]
     tags = ["key", "Shopify"]
 
 
 [[rules]]
 [[rules]]
+    id = "shopify-private"
     description = "Shopify private app access token"
     description = "Shopify private app access token"
     regex = '''shppa_[a-fA-F0-9]{32}'''
     regex = '''shppa_[a-fA-F0-9]{32}'''
     tags = ["key", "Shopify"]
     tags = ["key", "Shopify"]
 
 
 [[rules]]
 [[rules]]
+    id = "pypi"
     description = "PyPI upload token"
     description = "PyPI upload token"
     regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9-_]{50,1000}'''
     regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9-_]{50,1000}'''
     tags = ["key", "pypi"]
     tags = ["key", "pypi"]

+ 35 - 35
testdata/expected/report/sarif_simple.sarif

@@ -16,231 +16,231 @@
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "aws-secret-key",
        "name": "AWS Secret Key",
        "name": "AWS Secret Key",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)aws_(.{0,20})?=?.[\\'\\\"0-9a-zA-Z\\/+]{40}"
         "text": "(?i)aws_(.{0,20})?=?.[\\'\\\"0-9a-zA-Z\\/+]{40}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "aws-mws-key",
        "name": "AWS MWS key",
        "name": "AWS MWS key",
        "shortDescription": {
        "shortDescription": {
         "text": "amzn\\.mws\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
         "text": "amzn\\.mws\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "facebook-secret-key",
        "name": "Facebook Secret Key",
        "name": "Facebook Secret Key",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)(facebook|fb)(.{0,20})?(?-i)['\\\"][0-9a-f]{32}['\\\"]"
         "text": "(?i)(facebook|fb)(.{0,20})?(?-i)['\\\"][0-9a-f]{32}['\\\"]"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "facebook-client-id",
        "name": "Facebook Client ID",
        "name": "Facebook Client ID",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)(facebook|fb)(.{0,20})?['\\\"][0-9]{13,17}['\\\"]"
         "text": "(?i)(facebook|fb)(.{0,20})?['\\\"][0-9]{13,17}['\\\"]"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "twitter-secret-key",
        "name": "Twitter Secret Key",
        "name": "Twitter Secret Key",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)twitter(.{0,20})?['\\\"][0-9a-z]{35,44}['\\\"]"
         "text": "(?i)twitter(.{0,20})?['\\\"][0-9a-z]{35,44}['\\\"]"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "twitter-client-id",
        "name": "Twitter Client ID",
        "name": "Twitter Client ID",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)twitter(.{0,20})?['\\\"][0-9a-z]{18,25}['\\\"]"
         "text": "(?i)twitter(.{0,20})?['\\\"][0-9a-z]{18,25}['\\\"]"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "github-pat",
        "name": "Github Personal Access Token",
        "name": "Github Personal Access Token",
        "shortDescription": {
        "shortDescription": {
         "text": "ghp_[0-9a-zA-Z]{36}"
         "text": "ghp_[0-9a-zA-Z]{36}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "github-oauth",
        "name": "Github OAuth Access Token",
        "name": "Github OAuth Access Token",
        "shortDescription": {
        "shortDescription": {
         "text": "gho_[0-9a-zA-Z]{36}"
         "text": "gho_[0-9a-zA-Z]{36}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "github-app",
        "name": "Github App Token",
        "name": "Github App Token",
        "shortDescription": {
        "shortDescription": {
         "text": "(ghu|ghs)_[0-9a-zA-Z]{36}"
         "text": "(ghu|ghs)_[0-9a-zA-Z]{36}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "github-refresh",
        "name": "Github Refresh Token",
        "name": "Github Refresh Token",
        "shortDescription": {
        "shortDescription": {
         "text": "ghr_[0-9a-zA-Z]{76}"
         "text": "ghr_[0-9a-zA-Z]{76}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "linkedin-client",
        "name": "LinkedIn Client ID",
        "name": "LinkedIn Client ID",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)linkedin(.{0,20})?(?-i)[0-9a-z]{12}"
         "text": "(?i)linkedin(.{0,20})?(?-i)[0-9a-z]{12}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "linkedin-secret",
        "name": "LinkedIn Secret Key",
        "name": "LinkedIn Secret Key",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)linkedin(.{0,20})?[0-9a-z]{16}"
         "text": "(?i)linkedin(.{0,20})?[0-9a-z]{16}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "slack",
        "name": "Slack",
        "name": "Slack",
        "shortDescription": {
        "shortDescription": {
         "text": "xox[baprs]-([0-9a-zA-Z]{10,48})?"
         "text": "xox[baprs]-([0-9a-zA-Z]{10,48})?"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "apkey",
        "name": "Asymmetric Private Key",
        "name": "Asymmetric Private Key",
        "shortDescription": {
        "shortDescription": {
         "text": "-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----"
         "text": "-----BEGIN ((EC|PGP|DSA|RSA|OPENSSH) )?PRIVATE KEY( BLOCK)?-----"
        }
        }
       },
       },
       {
       {
-       "id": "",
-       "name": "Google API key",
+       "id": "google",
+       "name": "Google (GCP) Service Account",
        "shortDescription": {
        "shortDescription": {
-        "text": "AIza[0-9A-Za-z\\-_]{35}"
+        "text": "\"type\": \"service_account\""
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "google",
        "name": "Google (GCP) Service Account",
        "name": "Google (GCP) Service Account",
        "shortDescription": {
        "shortDescription": {
         "text": "\"type\": \"service_account\""
         "text": "\"type\": \"service_account\""
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "heroku",
        "name": "Heroku API key",
        "name": "Heroku API key",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)heroku(.{0,20})?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
         "text": "(?i)heroku(.{0,20})?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "mailchimp",
        "name": "MailChimp API key",
        "name": "MailChimp API key",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)(mailchimp|mc)(.{0,20})?[0-9a-f]{32}-us[0-9]{1,2}"
         "text": "(?i)(mailchimp|mc)(.{0,20})?[0-9a-f]{32}-us[0-9]{1,2}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "mailgun",
        "name": "Mailgun API key",
        "name": "Mailgun API key",
        "shortDescription": {
        "shortDescription": {
         "text": "((?i)(mailgun|mg)(.{0,20})?)?key-[0-9a-z]{32}"
         "text": "((?i)(mailgun|mg)(.{0,20})?)?key-[0-9a-z]{32}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "paypal",
        "name": "PayPal Braintree access token",
        "name": "PayPal Braintree access token",
        "shortDescription": {
        "shortDescription": {
         "text": "access_token\\$production\\$[0-9a-z]{16}\\$[0-9a-f]{32}"
         "text": "access_token\\$production\\$[0-9a-z]{16}\\$[0-9a-f]{32}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "piacatic",
        "name": "Picatic API key",
        "name": "Picatic API key",
        "shortDescription": {
        "shortDescription": {
         "text": "sk_live_[0-9a-z]{32}"
         "text": "sk_live_[0-9a-z]{32}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "sendgrid",
        "name": "SendGrid API Key",
        "name": "SendGrid API Key",
        "shortDescription": {
        "shortDescription": {
         "text": "SG\\.[\\w_]{16,32}\\.[\\w_]{16,64}"
         "text": "SG\\.[\\w_]{16,32}\\.[\\w_]{16,64}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "slack-webhook",
        "name": "Slack Webhook",
        "name": "Slack Webhook",
        "shortDescription": {
        "shortDescription": {
         "text": "https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8,12}/[a-zA-Z0-9_]{24}"
         "text": "https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8,12}/[a-zA-Z0-9_]{24}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "stripe",
        "name": "Stripe API key",
        "name": "Stripe API key",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)stripe(.{0,20})?[sr]k_live_[0-9a-zA-Z]{24}"
         "text": "(?i)stripe(.{0,20})?[sr]k_live_[0-9a-zA-Z]{24}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "square",
        "name": "Square access token",
        "name": "Square access token",
        "shortDescription": {
        "shortDescription": {
         "text": "sq0atp-[0-9A-Za-z\\-_]{22}"
         "text": "sq0atp-[0-9A-Za-z\\-_]{22}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "square-oauth",
        "name": "Square OAuth secret",
        "name": "Square OAuth secret",
        "shortDescription": {
        "shortDescription": {
         "text": "sq0csp-[0-9A-Za-z\\-_]{43}"
         "text": "sq0csp-[0-9A-Za-z\\-_]{43}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "twilio",
        "name": "Twilio API key",
        "name": "Twilio API key",
        "shortDescription": {
        "shortDescription": {
         "text": "(?i)twilio(.{0,20})?SK[0-9a-f]{32}"
         "text": "(?i)twilio(.{0,20})?SK[0-9a-f]{32}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "dynatrace",
        "name": "Dynatrace ttoken",
        "name": "Dynatrace ttoken",
        "shortDescription": {
        "shortDescription": {
         "text": "dt0[a-zA-Z]{1}[0-9]{2}\\.[A-Z0-9]{24}\\.[A-Z0-9]{64}"
         "text": "dt0[a-zA-Z]{1}[0-9]{2}\\.[A-Z0-9]{24}\\.[A-Z0-9]{64}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "shopify",
        "name": "Shopify shared secret",
        "name": "Shopify shared secret",
        "shortDescription": {
        "shortDescription": {
         "text": "shpss_[a-fA-F0-9]{32}"
         "text": "shpss_[a-fA-F0-9]{32}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "shopify-access",
        "name": "Shopify access token",
        "name": "Shopify access token",
        "shortDescription": {
        "shortDescription": {
         "text": "shpat_[a-fA-F0-9]{32}"
         "text": "shpat_[a-fA-F0-9]{32}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "shopify-custom",
        "name": "Shopify custom app access token",
        "name": "Shopify custom app access token",
        "shortDescription": {
        "shortDescription": {
         "text": "shpca_[a-fA-F0-9]{32}"
         "text": "shpca_[a-fA-F0-9]{32}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "shopify-private",
        "name": "Shopify private app access token",
        "name": "Shopify private app access token",
        "shortDescription": {
        "shortDescription": {
         "text": "shppa_[a-fA-F0-9]{32}"
         "text": "shppa_[a-fA-F0-9]{32}"
        }
        }
       },
       },
       {
       {
-       "id": "",
+       "id": "pypi",
        "name": "PyPI upload token",
        "name": "PyPI upload token",
        "shortDescription": {
        "shortDescription": {
         "text": "pypi-AgEIcHlwaS5vcmc[A-Za-z0-9-_]{50,1000}"
         "text": "pypi-AgEIcHlwaS5vcmc[A-Za-z0-9-_]{50,1000}"