config.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. package gitleaks
  2. import (
  3. "fmt"
  4. "os"
  5. "os/user"
  6. "regexp"
  7. "strconv"
  8. "strings"
  9. "github.com/BurntSushi/toml"
  10. "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
  11. )
  12. type entropyRange struct {
  13. v1 float64
  14. v2 float64
  15. }
  16. type Regex struct {
  17. description string
  18. regex *regexp.Regexp
  19. }
  20. // TomlConfig is used for loading gitleaks configs from a toml file
  21. type TomlConfig struct {
  22. Regexes []struct {
  23. Description string
  24. Regex string
  25. }
  26. Entropy struct {
  27. LineRegexes []string
  28. Ranges []string
  29. }
  30. Whitelist struct {
  31. Files []string
  32. Regexes []string
  33. Commits []string
  34. Repos []string
  35. }
  36. }
  37. // Config contains gitleaks config
  38. type Config struct {
  39. Regexes []Regex
  40. WhiteList struct {
  41. regexes []*regexp.Regexp
  42. files []*regexp.Regexp
  43. commits map[string]bool
  44. repos []*regexp.Regexp
  45. }
  46. Entropy struct {
  47. entropyRanges []*entropyRange
  48. regexes []*regexp.Regexp
  49. }
  50. sshAuth *ssh.PublicKeys
  51. }
  52. // loadToml loads of the toml config containing regexes and whitelists.
  53. // This function will first look if the configPath is set and load the config
  54. // from that file. Otherwise will then look for the path set by the GITHLEAKS_CONIFG
  55. // env var. If that is not set, then gitleaks will continue with the default configs
  56. // specified by the const var at the top `defaultConfig`
  57. func newConfig() (*Config, error) {
  58. var (
  59. tomlConfig TomlConfig
  60. configPath string
  61. config Config
  62. )
  63. if opts.ConfigPath != "" {
  64. configPath = opts.ConfigPath
  65. _, err := os.Stat(configPath)
  66. if err != nil {
  67. return nil, fmt.Errorf("no gitleaks config at %s", configPath)
  68. }
  69. } else {
  70. configPath = os.Getenv("GITLEAKS_CONFIG")
  71. }
  72. if configPath != "" {
  73. if _, err := toml.DecodeFile(configPath, &tomlConfig); err != nil {
  74. return nil, fmt.Errorf("problem loading config: %v", err)
  75. }
  76. } else {
  77. _, err := toml.Decode(defaultConfig, &tomlConfig)
  78. if err != nil {
  79. return nil, fmt.Errorf("problem loading default config: %v", err)
  80. }
  81. }
  82. sshAuth, err := getSSHAuth()
  83. if err != nil {
  84. return nil, err
  85. }
  86. config.sshAuth = sshAuth
  87. err = config.update(tomlConfig)
  88. if err != nil {
  89. return nil, err
  90. }
  91. return &config, err
  92. }
  93. // updateConfig will update a the global config values
  94. func (config *Config) update(tomlConfig TomlConfig) error {
  95. if len(tomlConfig.Entropy.Ranges) != 0 {
  96. err := config.updateEntropyRanges(tomlConfig.Entropy.Ranges)
  97. if err != nil {
  98. return err
  99. }
  100. }
  101. for _, regex := range tomlConfig.Entropy.LineRegexes {
  102. config.Entropy.regexes = append(config.Entropy.regexes, regexp.MustCompile(regex))
  103. }
  104. if singleSearchRegex != nil {
  105. config.Regexes = append(config.Regexes, Regex{
  106. description: "single search",
  107. regex: singleSearchRegex,
  108. })
  109. } else {
  110. for _, regex := range tomlConfig.Regexes {
  111. config.Regexes = append(config.Regexes, Regex{
  112. description: regex.Description,
  113. regex: regexp.MustCompile(regex.Regex),
  114. })
  115. }
  116. }
  117. config.WhiteList.commits = make(map[string]bool)
  118. for _, commit := range tomlConfig.Whitelist.Commits {
  119. config.WhiteList.commits[commit] = true
  120. }
  121. for _, regex := range tomlConfig.Whitelist.Files {
  122. config.WhiteList.files = append(config.WhiteList.files, regexp.MustCompile(regex))
  123. }
  124. for _, regex := range tomlConfig.Whitelist.Regexes {
  125. config.WhiteList.regexes = append(config.WhiteList.regexes, regexp.MustCompile(regex))
  126. }
  127. for _, regex := range tomlConfig.Whitelist.Repos {
  128. config.WhiteList.repos = append(config.WhiteList.repos, regexp.MustCompile(regex))
  129. }
  130. return nil
  131. }
  132. // entropyRanges hydrates entropyRanges which allows for fine tuning entropy checking
  133. func (config *Config) updateEntropyRanges(entropyLimitStr []string) error {
  134. for _, span := range entropyLimitStr {
  135. split := strings.Split(span, "-")
  136. v1, err := strconv.ParseFloat(split[0], 64)
  137. if err != nil {
  138. return err
  139. }
  140. v2, err := strconv.ParseFloat(split[1], 64)
  141. if err != nil {
  142. return err
  143. }
  144. if v1 > v2 {
  145. return fmt.Errorf("entropy range must be ascending")
  146. }
  147. r := &entropyRange{
  148. v1: v1,
  149. v2: v2,
  150. }
  151. if r.v1 > 8.0 || r.v1 < 0.0 || r.v2 > 8.0 || r.v2 < 0.0 {
  152. return fmt.Errorf("invalid entropy ranges, must be within 0.0-8.0")
  153. }
  154. config.Entropy.entropyRanges = append(config.Entropy.entropyRanges, r)
  155. }
  156. return nil
  157. }
  158. // externalConfig will attempt to load a pinned ".gitleaks.toml" configuration file
  159. // from a remote or local repo. Use the --repo-config option to trigger this.
  160. func (config *Config) updateFromRepo(repo *RepoInfo) error {
  161. var tomlConfig TomlConfig
  162. wt, err := repo.repository.Worktree()
  163. if err != nil {
  164. return err
  165. }
  166. f, err := wt.Filesystem.Open(".gitleaks.toml")
  167. if err != nil {
  168. return err
  169. }
  170. if _, err := toml.DecodeReader(f, &config); err != nil {
  171. return fmt.Errorf("problem loading config: %v", err)
  172. }
  173. f.Close()
  174. if err != nil {
  175. return err
  176. }
  177. return config.update(tomlConfig)
  178. }
  179. // getSSHAuth return an ssh auth use by go-git to clone repos behind authentication.
  180. // If --ssh-key is set then it will attempt to load the key from that path. If not,
  181. // gitleaks will use the default $HOME/.ssh/id_rsa key
  182. func getSSHAuth() (*ssh.PublicKeys, error) {
  183. var (
  184. sshKeyPath string
  185. )
  186. if opts.SSHKey != "" {
  187. sshKeyPath = opts.SSHKey
  188. } else {
  189. // try grabbing default
  190. c, err := user.Current()
  191. if err != nil {
  192. return nil, nil
  193. }
  194. sshKeyPath = fmt.Sprintf("%s/.ssh/id_rsa", c.HomeDir)
  195. }
  196. sshAuth, err := ssh.NewPublicKeysFromFile("git", sshKeyPath, "")
  197. if err != nil {
  198. if strings.HasPrefix(opts.Repo, "git") {
  199. // if you are attempting to clone a git repo via ssh and supply a bad ssh key,
  200. // the clone will fail.
  201. return nil, fmt.Errorf("unable to generate ssh key: %v", err)
  202. }
  203. }
  204. return sshAuth, nil
  205. }