config.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package config
  2. import (
  3. "fmt"
  4. "path"
  5. "regexp"
  6. "strconv"
  7. "github.com/zricethezav/gitleaks/v6/options"
  8. "github.com/BurntSushi/toml"
  9. log "github.com/sirupsen/logrus"
  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. Regexes []*regexp.Regexp
  16. Commits []string
  17. Files []*regexp.Regexp
  18. Paths []*regexp.Regexp
  19. Repos []*regexp.Regexp
  20. }
  21. // Entropy represents an entropy range
  22. type Entropy struct {
  23. Min float64
  24. Max float64
  25. Group int
  26. }
  27. // Rule is a struct that contains information that is loaded from a gitleaks config.
  28. // This struct is used in the Config struct as an array of Rules and is iterated
  29. // over during an scan. Each rule will be checked. If a regex match is found AND
  30. // that match is not allowlisted (globally or locally), then a leak will be appended
  31. // to the final scan report.
  32. type Rule struct {
  33. Description string
  34. Regex *regexp.Regexp
  35. File *regexp.Regexp
  36. Path *regexp.Regexp
  37. ReportGroup int
  38. Tags []string
  39. AllowList AllowList
  40. Entropies []Entropy
  41. }
  42. // Config is a composite struct of Rules and Allowlists
  43. // Each Rule contains a description, regular expression, tags, and allowlists if available
  44. type Config struct {
  45. Rules []Rule
  46. Allowlist AllowList
  47. }
  48. // TomlAllowList is a struct used in the TomlLoader that loads in allowlists from
  49. // specific rules or globally at the top level config
  50. type TomlAllowList struct {
  51. Description string
  52. Regexes []string
  53. Commits []string
  54. Files []string
  55. Paths []string
  56. Repos []string
  57. }
  58. // TomlLoader gets loaded with the values from a gitleaks toml config
  59. // see the config in config/defaults.go for an example. TomlLoader is used
  60. // to generate Config values (compiling regexes, etc).
  61. type TomlLoader struct {
  62. AllowList TomlAllowList
  63. Rules []struct {
  64. Description string
  65. Regex string
  66. File string
  67. Path string
  68. ReportGroup int
  69. Tags []string
  70. Entropies []struct {
  71. Min string
  72. Max string
  73. Group string
  74. }
  75. AllowList TomlAllowList
  76. }
  77. }
  78. // NewConfig will create a new config struct which contains
  79. // rules on how gitleaks will proceed with its scan.
  80. // If no options are passed via cli then NewConfig will return
  81. // a default config which can be seen in config.go
  82. func NewConfig(options options.Options) (Config, error) {
  83. var cfg Config
  84. tomlLoader := TomlLoader{}
  85. var err error
  86. if options.Config != "" {
  87. _, err = toml.DecodeFile(options.Config, &tomlLoader)
  88. // append a allowlist rule for allowlisting the config
  89. tomlLoader.AllowList.Files = append(tomlLoader.AllowList.Files, path.Base(options.Config))
  90. } else {
  91. _, err = toml.Decode(DefaultConfig, &tomlLoader)
  92. }
  93. if err != nil {
  94. return cfg, err
  95. }
  96. cfg, err = tomlLoader.Parse()
  97. if err != nil {
  98. return cfg, err
  99. }
  100. return cfg, nil
  101. }
  102. // Parse will parse the values set in a TomlLoader and use those values
  103. // to create compiled regular expressions and rules used in scans
  104. func (tomlLoader TomlLoader) Parse() (Config, error) {
  105. var cfg Config
  106. for _, rule := range tomlLoader.Rules {
  107. // check and make sure the rule is valid
  108. if rule.Regex == "" && rule.Path == "" && rule.File == "" && len(rule.Entropies) == 0 {
  109. log.Warnf("Rule %s does not define any actionable data", rule.Description)
  110. continue
  111. }
  112. re, err := regexp.Compile(rule.Regex)
  113. if err != nil {
  114. return cfg, fmt.Errorf("problem loading config: %v", err)
  115. }
  116. fileNameRe, err := regexp.Compile(rule.File)
  117. if err != nil {
  118. return cfg, fmt.Errorf("problem loading config: %v", err)
  119. }
  120. filePathRe, err := regexp.Compile(rule.Path)
  121. if err != nil {
  122. return cfg, fmt.Errorf("problem loading config: %v", err)
  123. }
  124. // rule specific allowlists
  125. var allowList AllowList
  126. // rule specific regexes
  127. for _, re := range rule.AllowList.Regexes {
  128. allowListedRegex, err := regexp.Compile(re)
  129. if err != nil {
  130. return cfg, fmt.Errorf("problem loading config: %v", err)
  131. }
  132. allowList.Regexes = append(allowList.Regexes, allowListedRegex)
  133. }
  134. // rule specific filenames
  135. for _, re := range rule.AllowList.Files {
  136. allowListedRegex, err := regexp.Compile(re)
  137. if err != nil {
  138. return cfg, fmt.Errorf("problem loading config: %v", err)
  139. }
  140. allowList.Files = append(allowList.Files, allowListedRegex)
  141. }
  142. // rule specific paths
  143. for _, re := range rule.AllowList.Paths {
  144. allowListedRegex, err := regexp.Compile(re)
  145. if err != nil {
  146. return cfg, fmt.Errorf("problem loading config: %v", err)
  147. }
  148. allowList.Paths = append(allowList.Paths, allowListedRegex)
  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. File: fileNameRe,
  181. Path: filePathRe,
  182. ReportGroup: rule.ReportGroup,
  183. Tags: rule.Tags,
  184. AllowList: allowList,
  185. Entropies: entropies,
  186. }
  187. cfg.Rules = append(cfg.Rules, r)
  188. }
  189. // global regex allowLists
  190. for _, allowListRegex := range tomlLoader.AllowList.Regexes {
  191. re, err := regexp.Compile(allowListRegex)
  192. if err != nil {
  193. return cfg, fmt.Errorf("problem loading config: %v", err)
  194. }
  195. cfg.Allowlist.Regexes = append(cfg.Allowlist.Regexes, re)
  196. }
  197. // global file name allowLists
  198. for _, allowListFileName := range tomlLoader.AllowList.Files {
  199. re, err := regexp.Compile(allowListFileName)
  200. if err != nil {
  201. return cfg, fmt.Errorf("problem loading config: %v", err)
  202. }
  203. cfg.Allowlist.Files = append(cfg.Allowlist.Files, re)
  204. }
  205. // global file path allowLists
  206. for _, allowListFilePath := range tomlLoader.AllowList.Paths {
  207. re, err := regexp.Compile(allowListFilePath)
  208. if err != nil {
  209. return cfg, fmt.Errorf("problem loading config: %v", err)
  210. }
  211. cfg.Allowlist.Paths = append(cfg.Allowlist.Paths, re)
  212. }
  213. // global repo allowLists
  214. for _, allowListRepo := range tomlLoader.AllowList.Repos {
  215. re, err := regexp.Compile(allowListRepo)
  216. if err != nil {
  217. return cfg, fmt.Errorf("problem loading config: %v", err)
  218. }
  219. cfg.Allowlist.Repos = append(cfg.Allowlist.Repos, re)
  220. }
  221. cfg.Allowlist.Commits = tomlLoader.AllowList.Commits
  222. cfg.Allowlist.Description = tomlLoader.AllowList.Description
  223. return cfg, nil
  224. }