rule.go 2.8 KB

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