config.go 6.6 KB

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