config.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. package config
  2. import (
  3. "fmt"
  4. log "github.com/sirupsen/logrus"
  5. "path"
  6. "regexp"
  7. "strconv"
  8. "github.com/zricethezav/gitleaks/v5/options"
  9. "github.com/BurntSushi/toml"
  10. )
  11. // Allowlist is struct containing items that if encountered will allowlist
  12. // a commit/line of code that would be considered a leak.
  13. type Allowlist struct {
  14. Description string
  15. Regex *regexp.Regexp
  16. File *regexp.Regexp
  17. Path *regexp.Regexp
  18. }
  19. // Entropy represents an entropy range
  20. type Entropy struct {
  21. Min float64
  22. Max float64
  23. Group int
  24. }
  25. // Rule is a struct that contains information that is loaded from a gitleaks config.
  26. // This struct is used in the Config struct as an array of Rules and is iterated
  27. // over during an scan. Each rule will be checked. If a regex match is found AND
  28. // that match is not allowlisted (globally or locally), then a leak will be appended
  29. // to the final scan report.
  30. type Rule struct {
  31. Description string
  32. Regex *regexp.Regexp
  33. FileNameRegex *regexp.Regexp
  34. FilePathRegex *regexp.Regexp
  35. Tags []string
  36. Allowlist []Allowlist
  37. Entropies []Entropy
  38. }
  39. // Config is a composite struct of Rules and Allowlists
  40. // Each Rule contains a description, regular expression, tags, and allowlists if available
  41. type Config struct {
  42. Rules []Rule
  43. Allowlist struct {
  44. Description string
  45. Commits []string
  46. Files []*regexp.Regexp
  47. Paths []*regexp.Regexp
  48. Repos []*regexp.Regexp
  49. }
  50. }
  51. // TomlLoader gets loaded with the values from a gitleaks toml config
  52. // see the config in config/defaults.go for an example. TomlLoader is used
  53. // to generate Config values (compiling regexes, etc).
  54. type TomlLoader struct {
  55. Allowlist struct {
  56. Description string
  57. Commits []string
  58. Files []string
  59. Paths []string
  60. Repos []string
  61. }
  62. Rules []struct {
  63. Description string
  64. Regex string
  65. FileNameRegex string
  66. FilePathRegex string
  67. Tags []string
  68. Entropies []struct {
  69. Min string
  70. Max string
  71. Group string
  72. }
  73. Allowlist []struct {
  74. Description string
  75. Regex string
  76. File string
  77. Path string
  78. }
  79. }
  80. }
  81. // NewConfig will create a new config struct which contains
  82. // rules on how gitleaks will proceed with its scan.
  83. // If no options are passed via cli then NewConfig will return
  84. // a default config which can be seen in config.go
  85. func NewConfig(options options.Options) (Config, error) {
  86. var cfg Config
  87. tomlLoader := TomlLoader{}
  88. var err error
  89. if options.Config != "" {
  90. _, err = toml.DecodeFile(options.Config, &tomlLoader)
  91. // append a allowlist rule for allowlisting the config
  92. tomlLoader.Allowlist.Files = append(tomlLoader.Allowlist.Files, path.Base(options.Config))
  93. } else {
  94. _, err = toml.Decode(DefaultConfig, &tomlLoader)
  95. }
  96. if err != nil {
  97. return cfg, err
  98. }
  99. cfg, err = tomlLoader.Parse()
  100. if err != nil {
  101. return cfg, err
  102. }
  103. return cfg, nil
  104. }
  105. // Parse will parse the values set in a TomlLoader and use those values
  106. // to create compiled regular expressions and rules used in scans
  107. func (tomlLoader TomlLoader) Parse() (Config, error) {
  108. var cfg Config
  109. for _, rule := range tomlLoader.Rules {
  110. // check and make sure the rule is valid
  111. if rule.Regex == "" && rule.FilePathRegex == "" && rule.FileNameRegex == "" && len(rule.Entropies) == 0 {
  112. log.Warnf("Rule %s does not define any actionable data", rule.Description)
  113. continue
  114. }
  115. re, err := regexp.Compile(rule.Regex)
  116. if err != nil {
  117. return cfg, fmt.Errorf("problem loading config: %v", err)
  118. }
  119. fileNameRe, err := regexp.Compile(rule.FileNameRegex)
  120. if err != nil {
  121. return cfg, fmt.Errorf("problem loading config: %v", err)
  122. }
  123. filePathRe, err := regexp.Compile(rule.FilePathRegex)
  124. if err != nil {
  125. return cfg, fmt.Errorf("problem loading config: %v", err)
  126. }
  127. // rule specific allowlists
  128. var allowlists []Allowlist
  129. for _, wl := range rule.Allowlist {
  130. wlRe, err := regexp.Compile(wl.Regex)
  131. if err != nil {
  132. return cfg, fmt.Errorf("problem loading config: %v", err)
  133. }
  134. wlFileNameRe, err := regexp.Compile(wl.File)
  135. if err != nil {
  136. return cfg, fmt.Errorf("problem loading config: %v", err)
  137. }
  138. wlFilePathRe, err := regexp.Compile(wl.Path)
  139. if err != nil {
  140. return cfg, fmt.Errorf("problem loading config: %v", err)
  141. }
  142. allowlists = append(allowlists, Allowlist{
  143. Description: wl.Description,
  144. File: wlFileNameRe,
  145. Path: wlFilePathRe,
  146. Regex: wlRe,
  147. })
  148. }
  149. var entropies []Entropy
  150. for _, e := range rule.Entropies {
  151. min, err := strconv.ParseFloat(e.Min, 64)
  152. if err != nil {
  153. return cfg, err
  154. }
  155. max, err := strconv.ParseFloat(e.Max, 64)
  156. if err != nil {
  157. return cfg, err
  158. }
  159. if e.Group == "" {
  160. e.Group = "0"
  161. }
  162. group, err := strconv.ParseInt(e.Group, 10, 64)
  163. if err != nil {
  164. return cfg, err
  165. } else if int(group) >= len(re.SubexpNames()) {
  166. return cfg, fmt.Errorf("problem loading config: group cannot be higher than number of groups in regexp")
  167. } else if group < 0 {
  168. return cfg, fmt.Errorf("problem loading config: group cannot be lower than 0")
  169. } else if min > 8.0 || min < 0.0 || max > 8.0 || max < 0.0 {
  170. return cfg, fmt.Errorf("problem loading config: invalid entropy ranges, must be within 0.0-8.0")
  171. } else if min > max {
  172. return cfg, fmt.Errorf("problem loading config: entropy Min value cannot be higher than Max value")
  173. }
  174. entropies = append(entropies, Entropy{Min: min, Max: max, Group: int(group)})
  175. }
  176. r := Rule{
  177. Description: rule.Description,
  178. Regex: re,
  179. FileNameRegex: fileNameRe,
  180. FilePathRegex: filePathRe,
  181. Tags: rule.Tags,
  182. Allowlist: allowlists,
  183. Entropies: entropies,
  184. }
  185. cfg.Rules = append(cfg.Rules, r)
  186. }
  187. // global file name allowlists
  188. for _, wlFileName := range tomlLoader.Allowlist.Files {
  189. re, err := regexp.Compile(wlFileName)
  190. if err != nil {
  191. return cfg, fmt.Errorf("problem loading config: %v", err)
  192. }
  193. cfg.Allowlist.Files = append(cfg.Allowlist.Files, re)
  194. }
  195. // global file path allowlists
  196. for _, wlFilePath := range tomlLoader.Allowlist.Paths {
  197. re, err := regexp.Compile(wlFilePath)
  198. if err != nil {
  199. return cfg, fmt.Errorf("problem loading config: %v", err)
  200. }
  201. cfg.Allowlist.Paths = append(cfg.Allowlist.Paths, re)
  202. }
  203. // global repo allowlists
  204. for _, wlRepo := range tomlLoader.Allowlist.Repos {
  205. re, err := regexp.Compile(wlRepo)
  206. if err != nil {
  207. return cfg, fmt.Errorf("problem loading config: %v", err)
  208. }
  209. cfg.Allowlist.Repos = append(cfg.Allowlist.Repos, re)
  210. }
  211. cfg.Allowlist.Commits = tomlLoader.Allowlist.Commits
  212. cfg.Allowlist.Description = tomlLoader.Allowlist.Description
  213. return cfg, nil
  214. }