rule.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. package config
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/zricethezav/gitleaks/v8/regexp"
  6. )
  7. // Rules contain information that define details on how to detect secrets
  8. type Rule struct {
  9. // RuleID is a unique identifier for this rule
  10. RuleID string
  11. // Description is the description of the rule.
  12. Description string
  13. // Entropy is a float representing the minimum shannon
  14. // entropy a regex group must have to be considered a secret.
  15. Entropy float64
  16. // SecretGroup is an int used to extract secret from regex
  17. // match and used as the group that will have its entropy
  18. // checked if `entropy` is set.
  19. SecretGroup int
  20. // Regex is a golang regular expression used to detect secrets.
  21. Regex *regexp.Regexp
  22. // Path is a golang regular expression used to
  23. // filter secrets by path
  24. Path *regexp.Regexp
  25. // Tags is an array of strings used for metadata
  26. // and reporting purposes.
  27. Tags []string
  28. // Keywords are used for pre-regex check filtering. Rules that contain
  29. // keywords will perform a quick string compare check to make sure the
  30. // keyword(s) are in the content being scanned.
  31. Keywords []string
  32. // Allowlists allows a rule to be ignored for specific commits, paths, regexes, and/or stopwords.
  33. Allowlists []*Allowlist
  34. // validated is an internal flag to track whether `Validate()` has been called.
  35. validated bool
  36. }
  37. // Validate guards against common misconfigurations.
  38. func (r *Rule) Validate() error {
  39. if r.validated {
  40. return nil
  41. }
  42. // Ensure |id| is present.
  43. if strings.TrimSpace(r.RuleID) == "" {
  44. // Try to provide helpful context, since |id| is empty.
  45. var context string
  46. if r.Regex != nil {
  47. context = ", regex: " + r.Regex.String()
  48. } else if r.Path != nil {
  49. context = ", path: " + r.Path.String()
  50. } else if r.Description != "" {
  51. context = ", description: " + r.Description
  52. }
  53. return fmt.Errorf("rule |id| is missing or empty" + context)
  54. }
  55. // Ensure the rule actually matches something.
  56. if r.Regex == nil && r.Path == nil {
  57. return fmt.Errorf("%s: both |regex| and |path| are empty, this rule will have no effect", r.RuleID)
  58. }
  59. // Ensure |secretGroup| works.
  60. if r.Regex != nil && r.SecretGroup > r.Regex.NumSubexp() {
  61. return fmt.Errorf("%s: invalid regex secret group %d, max regex secret group %d", r.RuleID, r.SecretGroup, r.Regex.NumSubexp())
  62. }
  63. for _, allowlist := range r.Allowlists {
  64. // This will probably never happen.
  65. if allowlist == nil {
  66. continue
  67. }
  68. if err := allowlist.Validate(); err != nil {
  69. return fmt.Errorf("%s: %w", r.RuleID, err)
  70. }
  71. }
  72. r.validated = true
  73. return nil
  74. }