Explorar el Código

feat(rule): validate & sort rule when generating (#1817)

Richard Gomez hace 10 meses
padre
commit
cfdeb0d7e0

+ 1 - 1
cmd/generate/config/base/config.go

@@ -11,7 +11,7 @@ import (
 func CreateGlobalConfig() config.Config {
 	return config.Config{
 		Title: "gitleaks config",
-		Allowlist: config.Allowlist{
+		Allowlist: &config.Allowlist{
 			Description: "global allow lists",
 			Regexes: []*regexp.Regexp{
 				// ----------- General placeholders -----------

+ 20 - 2
cmd/generate/config/main.go

@@ -1,6 +1,7 @@
 package main
 
 import (
+	"golang.org/x/exp/slices"
 	"os"
 	"text/template"
 
@@ -18,7 +19,7 @@ const (
 
 func main() {
 	if len(os.Args) < 2 {
-		os.Stderr.WriteString("Specify path to the gitleaks.toml config\n")
+		_, _ = os.Stderr.WriteString("Specify path to the gitleaks.toml config\n")
 		os.Exit(2)
 	}
 	gitleaksConfigPath := os.Args[1]
@@ -239,9 +240,18 @@ func main() {
 	// ensure rules have unique ids
 	ruleLookUp := make(map[string]config.Rule, len(configRules))
 	for _, rule := range configRules {
+		if err := rule.Validate(); err != nil {
+			logging.Fatal().Err(err).
+				Str("rule-id", rule.RuleID).
+				Msg("Failed to validate rule")
+		}
+		sortRule(rule)
+
 		// check if rule is in ruleLookUp
 		if _, ok := ruleLookUp[rule.RuleID]; ok {
-			logging.Fatal().Msgf("rule id %s is not unique", rule.RuleID)
+			logging.Fatal().
+				Str("rule-id", rule.RuleID).
+				Msg("rule id is not unique")
 		}
 		// TODO: eventually change all the signatures to get ride of this
 		// nasty dereferencing.
@@ -257,6 +267,7 @@ func main() {
 	if err != nil {
 		logging.Fatal().Err(err).Msg("Failed to create rules.toml")
 	}
+	defer f.Close()
 
 	cfg := base.CreateGlobalConfig()
 	cfg.Rules = ruleLookUp
@@ -264,3 +275,10 @@ func main() {
 		logging.Fatal().Err(err).Msg("could not execute template")
 	}
 }
+
+// sortRule makes the generated config deterministic.
+func sortRule(r *config.Rule) {
+	for _, a := range r.Allowlists {
+		slices.Sort(a.StopWords)
+	}
+}

+ 1 - 1
cmd/generate/config/rules/aws.go

@@ -22,7 +22,7 @@ func AWS() *config.Rule {
 			"ABIA", // AWS STS service bearer token
 			"ACCA", // Context-specific credential
 		},
-		Allowlists: []config.Allowlist{
+		Allowlists: []*config.Allowlist{
 			{
 				Regexes: []*regexp.Regexp{
 					regexp.MustCompile(`.+EXAMPLE$`),

+ 2 - 2
cmd/generate/config/rules/curl.go

@@ -16,7 +16,7 @@ func CurlBasicAuth() *config.Rule {
 		Regex:       regexp.MustCompile(`\bcurl\b(?:.*|.*(?:[\r\n]{1,2}.*){1,5})[ \t\n\r](?:-u|--user)(?:=|[ \t]{0,5})("(:[^"]{3,}|[^:"]{3,}:|[^:"]{3,}:[^"]{3,})"|'([^:']{3,}:[^']{3,})'|((?:"[^"]{3,}"|'[^']{3,}'|[\w$@.-]+):(?:"[^"]{3,}"|'[^']{3,}'|[\w${}@.-]+)))(?:\s|\z)`),
 		Keywords:    []string{"curl"},
 		Entropy:     2,
-		Allowlists: []config.Allowlist{
+		Allowlists: []*config.Allowlist{
 			{
 				Regexes: []*regexp.Regexp{
 					regexp.MustCompile(`[^:]+:(?:change(?:it|me)|pass(?:word)?|pwd|test|token|\*+|x+)`), // common placeholder passwords
@@ -109,7 +109,7 @@ func CurlHeaderAuth() *config.Rule {
 			fmt.Sprintf(`\bcurl\b(?:.*?|.*?(?:[\r\n]{1,2}.*?){1,5})[ \t\n\r](?:-H|--header)(?:=|[ \t]{0,5})(?:"%s"|'%s')(?:\B|\s|\z)`, authPat, authPat)),
 		Entropy:  2.75,
 		Keywords: []string{"curl"},
-		//Allowlists: []config.Allowlist{
+		//Allowlists: []*config.Allowlist{
 		//	{
 		//		Regexes: []*regexp.Regexp{},
 		//	},

+ 1 - 1
cmd/generate/config/rules/gcp.go

@@ -34,7 +34,7 @@ func GCPAPIKey() *config.Rule {
 		Keywords: []string{
 			"AIza",
 		},
-		Allowlists: []config.Allowlist{
+		Allowlists: []*config.Allowlist{
 			{
 				Regexes: []*regexp.Regexp{
 					// example keys from https://github.com/firebase/firebase-android-sdk

+ 2 - 2
cmd/generate/config/rules/generic.go

@@ -36,7 +36,7 @@ func GenericCredential() *config.Rule {
 			"token",
 		},
 		Entropy: 3.5,
-		Allowlists: []config.Allowlist{
+		Allowlists: []*config.Allowlist{
 			{
 				// NOTE: this is a goofy hack to get around the fact there golang's regex engine does not support positive lookaheads.
 				// Ideally we would want to ensure the secret contains both numbers and alphabetical characters, not just alphabetical characters.
@@ -281,7 +281,7 @@ jdbc.snowflake.url=`,
 }
 
 func newPlausibleSecret(regex string) string {
-	allowList := config.Allowlist{StopWords: DefaultStopWords}
+	allowList := &config.Allowlist{StopWords: DefaultStopWords}
 	// attempt to generate a random secret,
 	// retrying until it contains at least one digit and no stop words
 	// TODO: currently the DefaultStopWords list contains many short words,

+ 1 - 1
cmd/generate/config/rules/github.go

@@ -7,7 +7,7 @@ import (
 	"github.com/zricethezav/gitleaks/v8/regexp"
 )
 
-var githubAllowlist = []config.Allowlist{
+var githubAllowlist = []*config.Allowlist{
 	{
 		Paths: []*regexp.Regexp{
 			// https://github.com/octokit/auth-token.js/?tab=readme-ov-file#createtokenauthtoken-options

+ 1 - 1
cmd/generate/config/rules/hashicorp_vault.go

@@ -15,7 +15,7 @@ func VaultServiceToken() *config.Rule {
 		Regex:       utils.GenerateUniqueTokenRegex(`(?:hvs\.[\w-]{90,120}|s\.(?i:[a-z0-9]{24}))`, false),
 		Entropy:     3.5,
 		Keywords:    []string{"hvs.", "s."},
-		Allowlists: []config.Allowlist{
+		Allowlists: []*config.Allowlist{
 			{
 				Regexes: []*regexp.Regexp{
 					// https://github.com/gitleaks/gitleaks/issues/1490#issuecomment-2334166357

+ 1 - 1
cmd/generate/config/rules/kubernetes.go

@@ -31,7 +31,7 @@ func KubernetesSecret() *config.Rule {
 		},
 		// Kubernetes secrets are usually yaml files.
 		Path: regexp.MustCompile(`(?i)\.ya?ml$`),
-		Allowlists: []config.Allowlist{
+		Allowlists: []*config.Allowlist{
 			{
 				Regexes: []*regexp.Regexp{
 					// Ignore empty or placeholder values.

+ 1 - 1
cmd/generate/config/rules/nuget.go

@@ -14,7 +14,7 @@ func NugetConfigPassword() *config.Rule {
 		Path:        regexp.MustCompile(`(?i)nuget\.config$`),
 		Keywords:    []string{"<add key="},
 		Entropy:     1,
-		Allowlists: []config.Allowlist{
+		Allowlists: []*config.Allowlist{
 			{
 				Regexes: []*regexp.Regexp{
 					// samples from https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file

+ 22 - 4
config/allowlist.go

@@ -38,9 +38,6 @@ type Allowlist struct {
 	// Paths is a slice of path regular expressions that are allowed to be ignored.
 	Paths []*regexp.Regexp
 
-	// Regexes is slice of content regular expressions that are allowed to be ignored.
-	Regexes []*regexp.Regexp
-
 	// Can be `match` or `line`.
 	//
 	// If `match` the _Regexes_ will be tested against the match of the _Rule.Regex_.
@@ -50,15 +47,21 @@ type Allowlist struct {
 	// If RegexTarget is empty, it will be tested against the found secret.
 	RegexTarget string
 
+	// Regexes is slice of content regular expressions that are allowed to be ignored.
+	Regexes []*regexp.Regexp
+
 	// StopWords is a slice of stop words that are allowed to be ignored.
 	// This targets the _secret_, not the content of the regex match like the
 	// Regexes slice.
 	StopWords []string
+
+	// validated is an internal flag to track whether `Validate()` has been called.
+	validated bool
 }
 
 // CommitAllowed returns true if the commit is allowed to be ignored.
 func (a *Allowlist) CommitAllowed(c string) (bool, string) {
-	if c == "" {
+	if a == nil || c == "" {
 		return false, ""
 	}
 
@@ -72,15 +75,25 @@ func (a *Allowlist) CommitAllowed(c string) (bool, string) {
 
 // PathAllowed returns true if the path is allowed to be ignored.
 func (a *Allowlist) PathAllowed(path string) bool {
+	if a == nil || path == "" {
+		return false
+	}
 	return anyRegexMatch(path, a.Paths)
 }
 
 // RegexAllowed returns true if the regex is allowed to be ignored.
 func (a *Allowlist) RegexAllowed(secret string) bool {
+	if a == nil || secret == "" {
+		return false
+	}
 	return anyRegexMatch(secret, a.Regexes)
 }
 
 func (a *Allowlist) ContainsStopWord(s string) (bool, string) {
+	if a == nil || s == "" {
+		return false, ""
+	}
+
 	s = strings.ToLower(s)
 	for _, stopWord := range a.StopWords {
 		if strings.Contains(s, strings.ToLower(stopWord)) {
@@ -91,6 +104,10 @@ func (a *Allowlist) ContainsStopWord(s string) (bool, string) {
 }
 
 func (a *Allowlist) Validate() error {
+	if a.validated {
+		return nil
+	}
+
 	// Disallow empty allowlists.
 	if len(a.Commits) == 0 &&
 		len(a.Paths) == 0 &&
@@ -116,5 +133,6 @@ func (a *Allowlist) Validate() error {
 		a.StopWords = maps.Keys(uniqueStopwords)
 	}
 
+	a.validated = true
 	return nil
 }

+ 1 - 0
config/allowlist_test.go

@@ -148,6 +148,7 @@ func TestValidate(t *testing.T) {
 			opts = cmp.Options{
 				cmp.Comparer(regexComparer),
 				cmpopts.SortSlices(arrayComparer),
+				cmpopts.IgnoreUnexported(Allowlist{}),
 			}
 		)
 		if diff := cmp.Diff(tt.input, tt.expected, opts); diff != "" {

+ 6 - 8
config/config.go

@@ -67,7 +67,7 @@ type Config struct {
 	Path        string
 	Description string
 	Rules       map[string]Rule
-	Allowlist   Allowlist
+	Allowlist   *Allowlist
 	Keywords    map[string]struct{}
 
 	// used to keep sarif results consistent
@@ -164,17 +164,15 @@ func (vc *ViperConfig) Translate() (Config, error) {
 				allowlistPaths = append(allowlistPaths, regexp.MustCompile(a))
 			}
 
-			allowlist := Allowlist{
+			allowlist := &Allowlist{
+				Description:    a.Description,
 				MatchCondition: condition,
+				Commits:        a.Commits,
+				Paths:          allowlistPaths,
 				RegexTarget:    a.RegexTarget,
 				Regexes:        allowlistRegexes,
-				Paths:          allowlistPaths,
-				Commits:        a.Commits,
 				StopWords:      a.StopWords,
 			}
-			if err := allowlist.Validate(); err != nil {
-				return Config{}, fmt.Errorf("%s: %w", cr.RuleID, err)
-			}
 			cr.Allowlists = append(cr.Allowlists, allowlist)
 		}
 		orderedRules = append(orderedRules, cr.RuleID)
@@ -192,7 +190,7 @@ func (vc *ViperConfig) Translate() (Config, error) {
 		Description: vc.Description,
 		Extend:      vc.Extend,
 		Rules:       rulesMap,
-		Allowlist: Allowlist{
+		Allowlist: &Allowlist{
 			RegexTarget: vc.Allowlist.RegexTarget,
 			Regexes:     allowlistRegexes,
 			Paths:       allowlistPaths,

+ 13 - 8
config/config_test.go

@@ -2,6 +2,7 @@ package config
 
 import (
 	"fmt"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -33,7 +34,7 @@ func TestTranslate(t *testing.T) {
 					Regex:    regexp.MustCompile(`example\d+`),
 					Tags:     []string{},
 					Keywords: []string{},
-					Allowlists: []Allowlist{
+					Allowlists: []*Allowlist{
 						{
 							MatchCondition: AllowlistMatchOr,
 							Regexes:        []*regexp.Regexp{regexp.MustCompile("123")},
@@ -66,7 +67,7 @@ func TestTranslate(t *testing.T) {
 					Regex:       regexp.MustCompile("(?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}"),
 					Keywords:    []string{},
 					Tags:        []string{"key", "AWS"},
-					Allowlists: []Allowlist{
+					Allowlists: []*Allowlist{
 						{
 							MatchCondition: AllowlistMatchOr,
 							Regexes:        []*regexp.Regexp{regexp.MustCompile("AKIALALEMEL33243OLIA")},
@@ -84,7 +85,7 @@ func TestTranslate(t *testing.T) {
 					Regex:       regexp.MustCompile("(?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}"),
 					Keywords:    []string{},
 					Tags:        []string{"key", "AWS"},
-					Allowlists: []Allowlist{
+					Allowlists: []*Allowlist{
 						{
 							MatchCondition: AllowlistMatchOr,
 							Commits:        []string{"allowthiscommit"},
@@ -102,7 +103,7 @@ func TestTranslate(t *testing.T) {
 					Regex:       regexp.MustCompile("(?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}"),
 					Keywords:    []string{},
 					Tags:        []string{"key", "AWS"},
-					Allowlists: []Allowlist{
+					Allowlists: []*Allowlist{
 						{
 							MatchCondition: AllowlistMatchOr,
 							Paths:          []*regexp.Regexp{regexp.MustCompile(".go")},
@@ -178,7 +179,7 @@ func TestTranslate(t *testing.T) {
 						Regex:       regexp.MustCompile(`(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}`),
 						Keywords:    []string{},
 						Tags:        []string{"key", "AWS"},
-						Allowlists: []Allowlist{
+						Allowlists: []*Allowlist{
 							{
 								MatchCondition: AllowlistMatchOr,
 								StopWords:      []string{"fake"},
@@ -206,7 +207,7 @@ func TestTranslate(t *testing.T) {
 						Regex:       regexp.MustCompile(`(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}`),
 						Keywords:    []string{},
 						Tags:        []string{"key", "AWS"},
-						Allowlists: []Allowlist{
+						Allowlists: []*Allowlist{
 							{
 								MatchCondition: AllowlistMatchOr,
 								StopWords:      []string{"fake"},
@@ -234,8 +235,9 @@ func TestTranslate(t *testing.T) {
 						Regex:       regexp.MustCompile(`(?i)aws_(.{0,20})?=?.[\'\"0-9a-zA-Z\/+]{40}`),
 						Keywords:    []string{},
 						Tags:        []string{"key", "AWS"},
-						Allowlists: []Allowlist{
+						Allowlists: []*Allowlist{
 							{
+								Description:    "False positive. Keys used for colors match the rule, and should be excluded.",
 								MatchCondition: AllowlistMatchOr,
 								Paths:          []*regexp.Regexp{regexp.MustCompile(`something.py`)},
 							},
@@ -401,7 +403,10 @@ func TestTranslate(t *testing.T) {
 				}
 				return x.String() == y.String()
 			}
-			opts := cmp.Options{cmp.Comparer(regexComparer)}
+			opts := cmp.Options{
+				cmp.Comparer(regexComparer),
+				cmpopts.IgnoreUnexported(Rule{}, Allowlist{}),
+			}
 			if diff := cmp.Diff(tt.cfg.Rules, cfg.Rules, opts); diff != "" {
 				t.Errorf("%s diff: (-want +got)\n%s", tt.cfgName, diff)
 			}

+ 5 - 36
config/gitleaks.toml

@@ -595,6 +595,8 @@ regexes = [
 ]
 stopwords = [
     "000000",
+    "6fe4476ee5a1832882e326b506d14126",
+    "_ec2_",
     "aaaaaa",
     "about",
     "abstract",
@@ -610,9 +612,9 @@ stopwords = [
     "activity",
     "adapter",
     "add-",
+    "add-on",
     "add.",
     "add_",
-    "add-on",
     "addon",
     "addres",
     "admin",
@@ -655,11 +657,8 @@ stopwords = [
     "any_",
     "apache",
     "app-",
-    "app-",
-    "app.",
     "app.",
     "app_",
-    "app_",
     "apple",
     "arch",
     "archive",
@@ -682,7 +681,6 @@ stopwords = [
     "aura",
     "auth",
     "author",
-    "author",
     "authorize",
     "auto",
     "automated",
@@ -803,7 +801,6 @@ stopwords = [
     "cli.",
     "cli_",
     "client",
-    "client",
     "clojure",
     "clone",
     "closure",
@@ -838,7 +835,6 @@ stopwords = [
     "concept",
     "conf",
     "config",
-    "config",
     "connect",
     "connector",
     "console",
@@ -897,7 +893,6 @@ stopwords = [
     "dead",
     "debian",
     "debug",
-    "debug",
     "debugger",
     "deck",
     "define",
@@ -929,11 +924,8 @@ stopwords = [
     "dns-",
     "dns_",
     "doc-",
-    "doc-",
-    "doc.",
     "doc.",
     "doc_",
-    "doc_",
     "docker",
     "docpad",
     "doctrine",
@@ -946,8 +938,8 @@ stopwords = [
     "dom.",
     "dom_",
     "domain",
-    "done",
     "don't",
+    "done",
     "dot-",
     "dot.",
     "dot_",
@@ -967,7 +959,6 @@ stopwords = [
     "dsl_",
     "dynamic",
     "easy",
-    "_ec2_",
     "ecdsa",
     "eclipse",
     "edit",
@@ -997,7 +988,6 @@ stopwords = [
     "event",
     "evented",
     "example",
-    "example",
     "exchange",
     "exercise",
     "experiment",
@@ -1245,10 +1235,7 @@ stopwords = [
     "koan",
     "kohana",
     "lab-",
-    "lab-",
     "lab.",
-    "lab.",
-    "lab_",
     "lab_",
     "lambda",
     "lamp",
@@ -1333,11 +1320,8 @@ stopwords = [
     "manifest",
     "manual",
     "map-",
-    "map-",
-    "map.",
     "map.",
     "map_",
-    "map_",
     "mapper",
     "mapping",
     "markdown",
@@ -1422,11 +1406,8 @@ stopwords = [
     "nette",
     "network",
     "new-",
-    "new-",
-    "new.",
     "new.",
     "new_",
-    "new_",
     "next",
     "nginx",
     "ninja",
@@ -1494,7 +1475,6 @@ stopwords = [
     "package",
     "packet",
     "page",
-    "page",
     "panel",
     "paper",
     "paperclip",
@@ -1611,8 +1591,8 @@ stopwords = [
     "readme",
     "ready",
     "real",
-    "reality",
     "real-time",
+    "reality",
     "realtime",
     "recipe",
     "recorder",
@@ -1678,7 +1658,6 @@ stopwords = [
     "rvm_",
     "salt",
     "sample",
-    "sample",
     "sandbox",
     "sas-",
     "sas.",
@@ -1729,7 +1708,6 @@ stopwords = [
     "set.",
     "set_",
     "setting",
-    "setting",
     "setup",
     "sha1",
     "sha2",
@@ -1852,20 +1830,13 @@ stopwords = [
     "synopsi",
     "syntax",
     "system",
-    "system",
-    "tab-",
     "tab-",
     "tab.",
-    "tab.",
-    "tab_",
     "tab_",
     "table",
     "tag-",
-    "tag-",
-    "tag.",
     "tag.",
     "tag_",
-    "tag_",
     "talk",
     "target",
     "task",
@@ -2003,7 +1974,6 @@ stopwords = [
     "website",
     "websocket",
     "welcome",
-    "welcome",
     "what",
     "what'",
     "when",
@@ -2070,7 +2040,6 @@ stopwords = [
     "zsh-",
     "zsh.",
     "zsh_",
-    "6fe4476ee5a1832882e326b506d14126",
 ]
 [[rules.allowlists]]
 regexTarget = "line"

+ 20 - 2
config/rule.go

@@ -41,11 +41,18 @@ type Rule struct {
 	Keywords []string
 
 	// Allowlists allows a rule to be ignored for specific commits, paths, regexes, and/or stopwords.
-	Allowlists []Allowlist
+	Allowlists []*Allowlist
+
+	// validated is an internal flag to track whether `Validate()` has been called.
+	validated bool
 }
 
 // Validate guards against common misconfigurations.
-func (r Rule) Validate() error {
+func (r *Rule) Validate() error {
+	if r.validated {
+		return nil
+	}
+
 	// Ensure |id| is present.
 	if strings.TrimSpace(r.RuleID) == "" {
 		// Try to provide helpful context, since |id| is empty.
@@ -70,5 +77,16 @@ func (r Rule) Validate() error {
 		return fmt.Errorf("%s: invalid regex secret group %d, max regex secret group %d", r.RuleID, r.SecretGroup, r.Regex.NumSubexp())
 	}
 
+	for _, allowlist := range r.Allowlists {
+		// This will probably never happen.
+		if allowlist == nil {
+			continue
+		}
+		if err := allowlist.Validate(); err != nil {
+			return fmt.Errorf("%s: %w", r.RuleID, err)
+		}
+	}
+
+	r.validated = true
 	return nil
 }

+ 23 - 20
detect/detect.go

@@ -473,26 +473,29 @@ MatchLoop:
 				continue
 			}
 		}
-		// check if the regexTarget is defined in the allowlist "regexes" entry
-		// or if the secret is in the list of stopwords
-		globalAllowlistTarget := finding.Secret
-		switch d.Config.Allowlist.RegexTarget {
-		case "match":
-			globalAllowlistTarget = finding.Match
-		case "line":
-			globalAllowlistTarget = currentLine
-		}
-		if d.Config.Allowlist.RegexAllowed(globalAllowlistTarget) {
-			logger.Trace().
-				Str("finding", globalAllowlistTarget).
-				Msg("skipping finding: global allowlist regex")
-			continue
-		} else if ok, word := d.Config.Allowlist.ContainsStopWord(finding.Secret); ok {
-			logger.Trace().
-				Str("finding", finding.Secret).
-				Str("allowed-stopword", word).
-				Msg("skipping finding: global allowlist stopword")
-			continue
+
+		if d.Config.Allowlist != nil {
+			// check if the regexTarget is defined in the allowlist "regexes" entry
+			// or if the secret is in the list of stopwords
+			globalAllowlistTarget := finding.Secret
+			switch d.Config.Allowlist.RegexTarget {
+			case "match":
+				globalAllowlistTarget = finding.Match
+			case "line":
+				globalAllowlistTarget = currentLine
+			}
+			if d.Config.Allowlist.RegexAllowed(globalAllowlistTarget) {
+				logger.Trace().
+					Str("finding", globalAllowlistTarget).
+					Msg("skipping finding: global allowlist regex")
+				continue
+			} else if ok, word := d.Config.Allowlist.ContainsStopWord(finding.Secret); ok {
+				logger.Trace().
+					Str("finding", finding.Secret).
+					Str("allowed-stopword", word).
+					Msg("skipping finding: global allowlist stopword")
+				continue
+			}
 		}
 
 		// check if the result matches any of the rule allowlists.

+ 24 - 24
detect/detect_test.go

@@ -915,7 +915,7 @@ func TestDetectWithSymlinks(t *testing.T) {
 func TestDetectRuleAllowlist(t *testing.T) {
 	cases := map[string]struct {
 		fragment  Fragment
-		allowlist config.Allowlist
+		allowlist *config.Allowlist
 		expected  []report.Finding
 	}{
 		// Commit / path
@@ -923,7 +923,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 			fragment: Fragment{
 				CommitSHA: "41edf1f7f612199f401ccfc3144c2ebd0d7aeb48",
 			},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				Commits: []string{"41edf1f7f612199f401ccfc3144c2ebd0d7aeb48"},
 			},
 		},
@@ -931,7 +931,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 			fragment: Fragment{
 				FilePath: "package-lock.json",
 			},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				Paths: []*regexp.Regexp{regexp.MustCompile(`package-lock.json`)},
 			},
 		},
@@ -940,7 +940,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 				CommitSHA: "41edf1f7f612199f401ccfc3144c2ebd0d7aeb48",
 				FilePath:  "package-lock.json",
 			},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchAnd,
 				Commits:        []string{"41edf1f7f612199f401ccfc3144c2ebd0d7aeb48"},
 				Paths:          []*regexp.Regexp{regexp.MustCompile(`package-lock.json`)},
@@ -951,7 +951,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 				CommitSHA: "41edf1f7f612199f401ccfc3144c2ebd0d7aeb48",
 				FilePath:  "package.json",
 			},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchAnd,
 				Commits:        []string{"41edf1f7f612199f401ccfc3144c2ebd0d7aeb48"},
 				Paths:          []*regexp.Regexp{regexp.MustCompile(`package-lock.json`)},
@@ -974,7 +974,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 				CommitSHA: "41edf1f7f612199f401ccfc3144c2ebd0d7aeb48",
 				FilePath:  "package-lock.json",
 			},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchAnd,
 				Commits:        []string{"41edf1f7f612199f401ccfc3144c2ebd0d7aeb48"},
 				Paths:          []*regexp.Regexp{regexp.MustCompile(`package-lock.json`)},
@@ -998,7 +998,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 				CommitSHA: "41edf1f7f612199f401ccfc3144c2ebd0d7aeb48",
 				FilePath:  "package-lock.json",
 			},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchOr,
 				Commits:        []string{"704178e7dca77ff143778a31cff0fc192d59b030"},
 				Paths:          []*regexp.Regexp{regexp.MustCompile(`package-lock.json`)},
@@ -1008,19 +1008,19 @@ func TestDetectRuleAllowlist(t *testing.T) {
 		// Regex / stopwords
 		"regex allowed": {
 			fragment: Fragment{},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				Regexes: []*regexp.Regexp{regexp.MustCompile(`(?i)summer.+`)},
 			},
 		},
 		"stopwords allowed": {
 			fragment: Fragment{},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				StopWords: []string{"summer"},
 			},
 		},
 		"regex AND stopword allowed": {
 			fragment: Fragment{},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchAnd,
 				Regexes:        []*regexp.Regexp{regexp.MustCompile(`(?i)summer.+`)},
 				StopWords:      []string{"2024"},
@@ -1031,7 +1031,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 				CommitSHA: "41edf1f7f612199f401ccfc3144c2ebd0d7aeb48",
 				FilePath:  "config.js",
 			},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchAnd,
 				Commits:        []string{"41edf1f7f612199f401ccfc3144c2ebd0d7aeb48"},
 				Paths:          []*regexp.Regexp{regexp.MustCompile(`config.js`)},
@@ -1043,7 +1043,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 			fragment: Fragment{
 				FilePath: "config.js",
 			},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchAnd,
 				Commits:        []string{"41edf1f7f612199f401ccfc3144c2ebd0d7aeb48"},
 				Paths:          []*regexp.Regexp{regexp.MustCompile(`config.js`)},
@@ -1065,7 +1065,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 		},
 		"regex AND stopword NOT allowed": {
 			fragment: Fragment{},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchAnd,
 				Regexes: []*regexp.Regexp{
 					regexp.MustCompile(`(?i)winter.+`),
@@ -1089,7 +1089,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 				CommitSHA: "a060c9d2d5e90c992763f1bd4c3cd2a6f121241b",
 				FilePath:  "config.js",
 			},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchAnd,
 				Commits:        []string{"41edf1f7f612199f401ccfc3144c2ebd0d7aeb48"},
 				Paths:          []*regexp.Regexp{regexp.MustCompile(`package-lock.json`)},
@@ -1111,7 +1111,7 @@ func TestDetectRuleAllowlist(t *testing.T) {
 		},
 		"regex OR stopword allowed": {
 			fragment: Fragment{},
-			allowlist: config.Allowlist{
+			allowlist: &config.Allowlist{
 				MatchCondition: config.AllowlistMatchOr,
 				Regexes:        []*regexp.Regexp{regexp.MustCompile(`(?i)summer.+`)},
 				StopWords:      []string{"winter"},
@@ -1126,7 +1126,7 @@ let password = 'Summer2024!';`
 			rule := config.Rule{
 				RuleID: "test-rule",
 				Regex:  regexp.MustCompile(`Summer2024!`),
-				Allowlists: []config.Allowlist{
+				Allowlists: []*config.Allowlist{
 					tc.allowlist,
 				},
 			}
@@ -1320,7 +1320,7 @@ func TestWindowsFileSeparator_RuleAllowlistPaths(t *testing.T) {
 			rule: config.Rule{
 				RuleID: "unix-rule",
 				Regex:  regexp.MustCompile(`s3cr3t`),
-				Allowlists: []config.Allowlist{
+				Allowlists: []*config.Allowlist{
 					{
 						Paths: []*regexp.Regexp{regexp.MustCompile(`(^|/)ignoreme(/.*)?$`)},
 					},
@@ -1336,7 +1336,7 @@ func TestWindowsFileSeparator_RuleAllowlistPaths(t *testing.T) {
 			rule: config.Rule{
 				RuleID: "windows-rule",
 				Regex:  regexp.MustCompile(`s3cr3t`),
-				Allowlists: []config.Allowlist{
+				Allowlists: []*config.Allowlist{
 					{
 						Paths: []*regexp.Regexp{regexp.MustCompile(`(^|\\)ignoreme(\\.*)?$`)},
 					},
@@ -1364,7 +1364,7 @@ func TestWindowsFileSeparator_RuleAllowlistPaths(t *testing.T) {
 			rule: config.Rule{
 				RuleID: "unix-rule",
 				Regex:  regexp.MustCompile(`value: "[^"]+"`),
-				Allowlists: []config.Allowlist{
+				Allowlists: []*config.Allowlist{
 					{
 						MatchCondition: config.AllowlistMatchAnd,
 						Paths:          []*regexp.Regexp{regexp.MustCompile(`(^|/)ignoreme(/.*)?$`)},
@@ -1382,7 +1382,7 @@ func TestWindowsFileSeparator_RuleAllowlistPaths(t *testing.T) {
 			rule: config.Rule{
 				RuleID: "windows-rule",
 				Regex:  regexp.MustCompile(`value: "[^"]+"`),
-				Allowlists: []config.Allowlist{
+				Allowlists: []*config.Allowlist{
 					{
 						MatchCondition: config.AllowlistMatchAnd,
 						Paths:          []*regexp.Regexp{regexp.MustCompile(`(^|\\)ignoreme(\\.*)?$`)},
@@ -1414,7 +1414,7 @@ func TestWindowsFileSeparator_RuleAllowlistPaths(t *testing.T) {
 			rule: config.Rule{
 				RuleID: "unix-rule",
 				Regex:  regexp.MustCompile(`s3cr3t`),
-				Allowlists: []config.Allowlist{
+				Allowlists: []*config.Allowlist{
 					{
 						Paths: []*regexp.Regexp{regexp.MustCompile(`(^|/)ignoreme(/.*)?$`)},
 					},
@@ -1431,7 +1431,7 @@ func TestWindowsFileSeparator_RuleAllowlistPaths(t *testing.T) {
 			rule: config.Rule{
 				RuleID: "windows-rule",
 				Regex:  regexp.MustCompile(`s3cr3t`),
-				Allowlists: []config.Allowlist{
+				Allowlists: []*config.Allowlist{
 					{
 						Paths: []*regexp.Regexp{regexp.MustCompile(`(^|\\)ignoreme(\\.*)?$`)},
 					},
@@ -1448,7 +1448,7 @@ func TestWindowsFileSeparator_RuleAllowlistPaths(t *testing.T) {
 			rule: config.Rule{
 				RuleID: "unix-rule",
 				Regex:  regexp.MustCompile(`value: "[^"]+"`),
-				Allowlists: []config.Allowlist{
+				Allowlists: []*config.Allowlist{
 					{
 						MatchCondition: config.AllowlistMatchAnd,
 						Paths:          []*regexp.Regexp{regexp.MustCompile(`(^|/)ignoreme(/.*)?$`)},
@@ -1467,7 +1467,7 @@ func TestWindowsFileSeparator_RuleAllowlistPaths(t *testing.T) {
 			rule: config.Rule{
 				RuleID: "windows-rule",
 				Regex:  regexp.MustCompile(`value: "[^"]+"`),
-				Allowlists: []config.Allowlist{
+				Allowlists: []*config.Allowlist{
 					{
 						MatchCondition: config.AllowlistMatchAnd,
 						Paths:          []*regexp.Regexp{regexp.MustCompile(`(^|\\)ignoreme(\\.*)?$`)},