config.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. package main
  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. const defaultConfig = `
  13. # This is a sample config file for gitleaks. You can configure gitleaks what to search for and what to whitelist.
  14. # The output you are seeing here is the default gitleaks config. If GITLEAKS_CONFIG environment variable
  15. # is set, gitleaks will load configurations from that path. If option --config-path is set, gitleaks will load
  16. # configurations from that path. Gitleaks does not whitelist anything by default.
  17. title = "gitleaks config"
  18. # add regexes to the regex table
  19. [[regexes]]
  20. description = "AWS"
  21. regex = '''AKIA[0-9A-Z]{16}'''
  22. [[regexes]]
  23. description = "PKCS8"
  24. regex = '''-----BEGIN PRIVATE KEY-----'''
  25. [[regexes]]
  26. description = "RSA"
  27. regex = '''-----BEGIN RSA PRIVATE KEY-----'''
  28. [[regexes]]
  29. description = "SSH"
  30. regex = '''-----BEGIN OPENSSH PRIVATE KEY-----'''
  31. [[regexes]]
  32. description = "PGP"
  33. regex = '''-----BEGIN PGP PRIVATE KEY BLOCK-----'''
  34. [[regexes]]
  35. description = "Facebook"
  36. regex = '''(?i)facebook(.{0,4})?['\"][0-9a-f]{32}['\"]'''
  37. [[regexes]]
  38. description = "Twitter"
  39. regex = '''(?i)twitter(.{0,4})?['\"][0-9a-zA-Z]{35,44}['\"]'''
  40. [[regexes]]
  41. description = "Github"
  42. regex = '''(?i)github(.{0,4})?['\"][0-9a-zA-Z]{35,40}['\"]'''
  43. [[regexes]]
  44. description = "Slack"
  45. regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})?'''
  46. [entropy]
  47. lineregexes = [
  48. "api",
  49. "key",
  50. "signature",
  51. "secret",
  52. "password",
  53. "pass",
  54. "pwd",
  55. "token",
  56. "curl",
  57. "wget",
  58. "https?",
  59. ]
  60. [whitelist]
  61. files = [
  62. "(.*?)(jpg|gif|doc|pdf|bin)$"
  63. ]
  64. #commits = [
  65. # "BADHA5H1",
  66. # "BADHA5H2",
  67. #]
  68. #repos = [
  69. # "mygoodrepo"
  70. #]
  71. [misc]
  72. #entropy = [
  73. # "3.3-4.30"
  74. # "6.0-8.0
  75. #]
  76. `
  77. type entropyRange struct {
  78. v1 float64
  79. v2 float64
  80. }
  81. type Regex struct {
  82. description string
  83. regex *regexp.Regexp
  84. }
  85. // TomlConfig is used for loading gitleaks configs from a toml file
  86. type TomlConfig struct {
  87. Regexes []struct {
  88. Description string
  89. Regex string
  90. }
  91. Entropy struct {
  92. LineRegexes []string
  93. }
  94. Whitelist struct {
  95. Files []string
  96. Regexes []string
  97. Commits []string
  98. Repos []string
  99. }
  100. Misc struct {
  101. Entropy []string
  102. }
  103. }
  104. // Config contains gitleaks config
  105. type Config struct {
  106. Regexes []Regex
  107. WhiteList struct {
  108. regexes []*regexp.Regexp
  109. files []*regexp.Regexp
  110. commits map[string]bool
  111. repos []*regexp.Regexp
  112. }
  113. Entropy struct {
  114. entropyRanges []*entropyRange
  115. regexes []*regexp.Regexp
  116. }
  117. sshAuth *ssh.PublicKeys
  118. }
  119. // loadToml loads of the toml config containing regexes and whitelists.
  120. // This function will first look if the configPath is set and load the config
  121. // from that file. Otherwise will then look for the path set by the GITHLEAKS_CONIFG
  122. // env var. If that is not set, then gitleaks will continue with the default configs
  123. // specified by the const var at the top `defaultConfig`
  124. func newConfig() (*Config, error) {
  125. var (
  126. tomlConfig TomlConfig
  127. configPath string
  128. config Config
  129. )
  130. if opts.ConfigPath != "" {
  131. configPath = opts.ConfigPath
  132. _, err := os.Stat(configPath)
  133. if err != nil {
  134. return nil, fmt.Errorf("no gitleaks config at %s", configPath)
  135. }
  136. } else {
  137. configPath = os.Getenv("GITLEAKS_CONFIG")
  138. }
  139. if configPath != "" {
  140. if _, err := toml.DecodeFile(configPath, &tomlConfig); err != nil {
  141. return nil, fmt.Errorf("problem loading config: %v", err)
  142. }
  143. } else {
  144. _, err := toml.Decode(defaultConfig, &tomlConfig)
  145. if err != nil {
  146. return nil, fmt.Errorf("problem loading default config: %v", err)
  147. }
  148. }
  149. sshAuth, err := getSSHAuth()
  150. if err != nil {
  151. return nil, err
  152. }
  153. config.sshAuth = sshAuth
  154. err = config.update(tomlConfig)
  155. if err != nil {
  156. return nil, err
  157. }
  158. return &config, err
  159. }
  160. // updateConfig will update a the global config values
  161. func (config *Config) update(tomlConfig TomlConfig) error {
  162. if len(tomlConfig.Misc.Entropy) != 0 {
  163. err := config.updateEntropyRanges(tomlConfig.Misc.Entropy)
  164. if err != nil {
  165. return err
  166. }
  167. }
  168. for _, regex := range tomlConfig.Entropy.LineRegexes {
  169. config.Entropy.regexes = append(config.Entropy.regexes, regexp.MustCompile(regex))
  170. }
  171. if singleSearchRegex != nil {
  172. config.Regexes = append(config.Regexes, Regex{
  173. description: "single search",
  174. regex: singleSearchRegex,
  175. })
  176. } else {
  177. for _, regex := range tomlConfig.Regexes {
  178. config.Regexes = append(config.Regexes, Regex{
  179. description: regex.Description,
  180. regex: regexp.MustCompile(regex.Regex),
  181. })
  182. }
  183. }
  184. config.WhiteList.commits = make(map[string]bool)
  185. for _, commit := range tomlConfig.Whitelist.Commits {
  186. config.WhiteList.commits[commit] = true
  187. }
  188. for _, regex := range tomlConfig.Whitelist.Files {
  189. config.WhiteList.files = append(config.WhiteList.files, regexp.MustCompile(regex))
  190. }
  191. for _, regex := range tomlConfig.Whitelist.Regexes {
  192. config.WhiteList.regexes = append(config.WhiteList.regexes, regexp.MustCompile(regex))
  193. }
  194. for _, regex := range tomlConfig.Whitelist.Repos {
  195. config.WhiteList.repos = append(config.WhiteList.repos, regexp.MustCompile(regex))
  196. }
  197. return nil
  198. }
  199. // entropyRanges hydrates entropyRanges which allows for fine tuning entropy checking
  200. func (config *Config) updateEntropyRanges(entropyLimitStr []string) error {
  201. for _, span := range entropyLimitStr {
  202. split := strings.Split(span, "-")
  203. v1, err := strconv.ParseFloat(split[0], 64)
  204. if err != nil {
  205. return err
  206. }
  207. v2, err := strconv.ParseFloat(split[1], 64)
  208. if err != nil {
  209. return err
  210. }
  211. if v1 > v2 {
  212. return fmt.Errorf("entropy range must be ascending")
  213. }
  214. r := &entropyRange{
  215. v1: v1,
  216. v2: v2,
  217. }
  218. if r.v1 > 8.0 || r.v1 < 0.0 || r.v2 > 8.0 || r.v2 < 0.0 {
  219. return fmt.Errorf("invalid entropy ranges, must be within 0.0-8.0")
  220. }
  221. config.Entropy.entropyRanges = append(config.Entropy.entropyRanges, r)
  222. }
  223. return nil
  224. }
  225. // externalConfig will attempt to load a pinned ".gitleaks.toml" configuration file
  226. // from a remote or local repo. Use the --repo-config option to trigger this.
  227. func (config *Config) updateFromRepo(repo *RepoDescriptor) error {
  228. var tomlConfig TomlConfig
  229. wt, err := repo.repository.Worktree()
  230. if err != nil {
  231. return err
  232. }
  233. f, err := wt.Filesystem.Open(".gitleaks.toml")
  234. if err != nil {
  235. return err
  236. }
  237. if _, err := toml.DecodeReader(f, &config); err != nil {
  238. return fmt.Errorf("problem loading config: %v", err)
  239. }
  240. f.Close()
  241. if err != nil {
  242. return err
  243. }
  244. return config.update(tomlConfig)
  245. }
  246. // getSSHAuth return an ssh auth use by go-git to clone repos behind authentication.
  247. // If --ssh-key is set then it will attempt to load the key from that path. If not,
  248. // gitleaks will use the default $HOME/.ssh/id_rsa key
  249. func getSSHAuth() (*ssh.PublicKeys, error) {
  250. var (
  251. sshKeyPath string
  252. )
  253. if opts.SSHKey != "" {
  254. sshKeyPath = opts.SSHKey
  255. } else {
  256. // try grabbing default
  257. c, err := user.Current()
  258. if err != nil {
  259. return nil, nil
  260. }
  261. sshKeyPath = fmt.Sprintf("%s/.ssh/id_rsa", c.HomeDir)
  262. }
  263. sshAuth, err := ssh.NewPublicKeysFromFile("git", sshKeyPath, "")
  264. if err != nil {
  265. if strings.HasPrefix(opts.Repo, "git") {
  266. // if you are attempting to clone a git repo via ssh and supply a bad ssh key,
  267. // the clone will fail.
  268. return nil, fmt.Errorf("unable to generate ssh key: %v", err)
  269. }
  270. }
  271. return sshAuth, nil
  272. }