rule.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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. identifierPrefix = `(?:`
  15. identifierSuffix = `)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}`
  16. // commonly used assignment operators or function call
  17. operator = `(?:=|>|:=|\|\|:|<=|=>|:)`
  18. // boundaries for the secret
  19. // \x60 = `
  20. secretPrefixUnique = `\b(`
  21. secretPrefix = `(?:'|\"|\s|=|\x60){0,5}(`
  22. secretSuffix = `)(?:['|\"|\n|\r|\s|\x60|;]|$)`
  23. )
  24. func generateSemiGenericRegex(identifiers []string, secretRegex string) *regexp.Regexp {
  25. var sb strings.Builder
  26. sb.WriteString(caseInsensitive)
  27. sb.WriteString(identifierPrefix)
  28. sb.WriteString(strings.Join(identifiers, "|"))
  29. sb.WriteString(identifierSuffix)
  30. sb.WriteString(operator)
  31. sb.WriteString(secretPrefix)
  32. sb.WriteString(secretRegex)
  33. sb.WriteString(secretSuffix)
  34. return regexp.MustCompile(sb.String())
  35. }
  36. func generateUniqueTokenRegex(secretRegex string) *regexp.Regexp {
  37. var sb strings.Builder
  38. sb.WriteString(caseInsensitive)
  39. sb.WriteString(secretPrefixUnique)
  40. sb.WriteString(secretRegex)
  41. sb.WriteString(secretSuffix)
  42. return regexp.MustCompile(sb.String())
  43. }
  44. func generateSampleSecret(identifier string, secret string) string {
  45. return fmt.Sprintf("%s_api_token = \"%s\"", identifier, secret)
  46. }
  47. func validate(r config.Rule, truePositives []string, falsePositives []string) *config.Rule {
  48. // normalize keywords like in the config package
  49. var keywords []string
  50. for _, k := range r.Keywords {
  51. keywords = append(keywords, strings.ToLower(k))
  52. }
  53. r.Keywords = keywords
  54. rules := make(map[string]config.Rule)
  55. rules[r.RuleID] = r
  56. d := detect.NewDetector(config.Config{
  57. Rules: rules,
  58. Keywords: keywords,
  59. })
  60. for _, tp := range truePositives {
  61. if len(d.DetectString(tp)) != 1 {
  62. log.Fatal().Msgf("Failed to validate. For rule ID [%s], true positive [%s] was not detected by regexp [%s]", r.RuleID, tp, r.Regex)
  63. }
  64. }
  65. for _, fp := range falsePositives {
  66. if len(d.DetectString(fp)) != 0 {
  67. log.Fatal().Msgf("Failed to validate (fp) [%s]", r.RuleID)
  68. }
  69. }
  70. return &r
  71. }
  72. func numeric(size string) string {
  73. return fmt.Sprintf(`[0-9]{%s}`, size)
  74. }
  75. func hex(size string) string {
  76. return fmt.Sprintf(`[a-f0-9]{%s}`, size)
  77. }
  78. func alphaNumeric(size string) string {
  79. return fmt.Sprintf(`[a-z0-9]{%s}`, size)
  80. }
  81. func alphaNumericExtendedShort(size string) string {
  82. return fmt.Sprintf(`[a-z0-9_-]{%s}`, size)
  83. }
  84. func alphaNumericExtended(size string) string {
  85. return fmt.Sprintf(`[a-z0-9=_\-]{%s}`, size)
  86. }
  87. func alphaNumericExtendedLong(size string) string {
  88. return fmt.Sprintf(`[a-z0-9\/=_\+\-]{%s}`, size)
  89. }
  90. func hex8_4_4_4_12() string {
  91. return `[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`
  92. }