rule.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package rules
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. "github.com/rs/zerolog/log"
  7. "github.com/zricethezav/gitleaks/v8/config"
  8. "github.com/zricethezav/gitleaks/v8/detect"
  9. )
  10. const (
  11. // case insensitive prefix
  12. caseInsensitive = `(?i)`
  13. // identifier prefix (just an ignore group)
  14. identifierCaseInsensitivePrefix = `(?i:`
  15. identifierCaseInsensitiveSuffix = `)`
  16. identifierPrefix = `(?:`
  17. identifierSuffix = `)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}`
  18. // commonly used assignment operators or function call
  19. operator = `(?:=|>|:{1,3}=|\|\|:|<=|=>|:|\?=)`
  20. // boundaries for the secret
  21. // \x60 = `
  22. secretPrefixUnique = `\b(`
  23. secretPrefix = `(?:'|\"|\s|=|\x60){0,5}(`
  24. secretSuffix = `)(?:['|\"|\n|\r|\s|\x60|;]|$)`
  25. )
  26. func generateSemiGenericRegex(identifiers []string, secretRegex string, isCaseInsensitive bool) *regexp.Regexp {
  27. var sb strings.Builder
  28. // The identifiers should always be case-insensitive.
  29. // This is inelegant but prevents an extraneous `(?i:)` from being added to the pattern; it could be removed.
  30. if isCaseInsensitive {
  31. sb.WriteString(caseInsensitive)
  32. writeIdentifiers(&sb, identifiers)
  33. } else {
  34. sb.WriteString(identifierCaseInsensitivePrefix)
  35. writeIdentifiers(&sb, identifiers)
  36. sb.WriteString(identifierCaseInsensitiveSuffix)
  37. }
  38. sb.WriteString(operator)
  39. sb.WriteString(secretPrefix)
  40. sb.WriteString(secretRegex)
  41. sb.WriteString(secretSuffix)
  42. return regexp.MustCompile(sb.String())
  43. }
  44. func writeIdentifiers(sb *strings.Builder, identifiers []string) {
  45. sb.WriteString(identifierPrefix)
  46. sb.WriteString(strings.Join(identifiers, "|"))
  47. sb.WriteString(identifierSuffix)
  48. }
  49. func generateUniqueTokenRegex(secretRegex string, isCaseInsensitive bool) *regexp.Regexp {
  50. var sb strings.Builder
  51. if isCaseInsensitive {
  52. sb.WriteString(caseInsensitive)
  53. }
  54. sb.WriteString(secretPrefixUnique)
  55. sb.WriteString(secretRegex)
  56. sb.WriteString(secretSuffix)
  57. return regexp.MustCompile(sb.String())
  58. }
  59. func generateSampleSecret(identifier string, secret string) string {
  60. return fmt.Sprintf("%s_api_token = \"%s\"", identifier, secret)
  61. }
  62. func validate(r config.Rule, truePositives []string, falsePositives []string) *config.Rule {
  63. // normalize keywords like in the config package
  64. var keywords []string
  65. for _, k := range r.Keywords {
  66. keywords = append(keywords, strings.ToLower(k))
  67. }
  68. r.Keywords = keywords
  69. rules := make(map[string]config.Rule)
  70. rules[r.RuleID] = r
  71. d := detect.NewDetector(config.Config{
  72. Rules: rules,
  73. Keywords: keywords,
  74. })
  75. for _, tp := range truePositives {
  76. if len(d.DetectString(tp)) != 1 {
  77. log.Fatal().Msgf("Failed to validate. For rule ID [%s], true positive [%s] was not detected by regexp [%s]", r.RuleID, tp, r.Regex)
  78. }
  79. }
  80. for _, fp := range falsePositives {
  81. if len(d.DetectString(fp)) != 0 {
  82. log.Fatal().Msgf("Failed to validate. For rule ID [%s], false positive [%s] was detected by regexp [%s]", r.RuleID, fp, r.Regex)
  83. }
  84. }
  85. return &r
  86. }
  87. func numeric(size string) string {
  88. return fmt.Sprintf(`[0-9]{%s}`, size)
  89. }
  90. func hex(size string) string {
  91. return fmt.Sprintf(`[a-f0-9]{%s}`, size)
  92. }
  93. func alphaNumeric(size string) string {
  94. return fmt.Sprintf(`[a-z0-9]{%s}`, size)
  95. }
  96. func alphaNumericExtendedShort(size string) string {
  97. return fmt.Sprintf(`[a-z0-9_-]{%s}`, size)
  98. }
  99. func alphaNumericExtended(size string) string {
  100. return fmt.Sprintf(`[a-z0-9=_\-]{%s}`, size)
  101. }
  102. func alphaNumericExtendedLong(size string) string {
  103. return fmt.Sprintf(`[a-z0-9\/=_\+\-]{%s}`, size)
  104. }
  105. func hex8_4_4_4_12() string {
  106. return `[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`
  107. }