main.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package main
  2. import (
  3. "context"
  4. "github.com/google/go-github/github"
  5. "github.com/mitchellh/go-homedir"
  6. "golang.org/x/oauth2"
  7. "io/ioutil"
  8. "log"
  9. "net/http"
  10. "os"
  11. "path"
  12. "path/filepath"
  13. "regexp"
  14. "strings"
  15. )
  16. var (
  17. regexes map[string]*regexp.Regexp
  18. stopWords []string
  19. base64Chars string
  20. hexChars string
  21. assignRegex *regexp.Regexp
  22. gitLeaksPath string
  23. gitLeaksClonePath string
  24. gitLeaksReportPath string
  25. )
  26. type RepoDesc struct {
  27. name string
  28. url string
  29. path string
  30. owner *Owner
  31. }
  32. type Owner struct {
  33. name string
  34. url string
  35. accountType string
  36. path string
  37. reportPath string
  38. }
  39. func init() {
  40. base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
  41. hexChars = "1234567890abcdefABCDEF"
  42. stopWords = []string{"setting", "Setting", "SETTING", "info",
  43. "Info", "INFO", "env", "Env", "ENV", "environment", "Environment", "ENVIRONMENT"}
  44. regexes = map[string]*regexp.Regexp{
  45. "RSA": regexp.MustCompile("-----BEGIN RSA PRIVATE KEY-----"),
  46. "SSH": regexp.MustCompile("-----BEGIN OPENSSH PRIVATE KEY-----"),
  47. "Facebook": regexp.MustCompile("(?i)facebook.*['|\"][0-9a-f]{32}['|\"]"),
  48. "Twitter": regexp.MustCompile("(?i)twitter.*['|\"][0-9a-zA-Z]{35,44}['|\"]"),
  49. "Github": regexp.MustCompile("(?i)github.*[['|\"]0-9a-zA-Z]{35,40}['|\"]"),
  50. "Reddit": regexp.MustCompile("(?i)reddit.*['|\"][0-9a-zA-Z]{14}['|\"]"),
  51. "Heroku": regexp.MustCompile("(?i)heroku.*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}"),
  52. "AWS": regexp.MustCompile("AKIA[0-9A-Z]{16}"),
  53. // "Custom": regexp.MustCompile(".*")
  54. }
  55. assignRegex = regexp.MustCompile(`(=|:|:=|<-)`)
  56. homeDir, err := homedir.Dir()
  57. if err != nil {
  58. log.Fatal("Cant find home dir")
  59. }
  60. gitLeaksPath = filepath.Join(homeDir, ".gitleaks")
  61. if _, err := os.Stat(gitLeaksPath); os.IsNotExist(err) {
  62. os.Mkdir(gitLeaksPath, os.ModePerm)
  63. }
  64. gitLeaksClonePath = filepath.Join(gitLeaksPath, "clones")
  65. if _, err := os.Stat(gitLeaksClonePath); os.IsNotExist(err) {
  66. os.Mkdir(gitLeaksClonePath, os.ModePerm)
  67. }
  68. gitLeaksReportPath = filepath.Join(gitLeaksPath, "report")
  69. if _, err := os.Stat(gitLeaksReportPath); os.IsNotExist(err) {
  70. os.Mkdir(gitLeaksReportPath, os.ModePerm)
  71. }
  72. }
  73. // getOwner
  74. func getOwner(opts *Options) *Owner {
  75. var owner Owner
  76. if opts.RepoURL != "" {
  77. splitSlashes := strings.Split(opts.RepoURL, "/")
  78. owner = Owner{
  79. name: splitSlashes[len(splitSlashes)-2],
  80. url: opts.RepoURL,
  81. accountType: "users",
  82. }
  83. } else if opts.UserURL != "" {
  84. _, ownerName := path.Split(opts.UserURL)
  85. owner = Owner{
  86. name: ownerName,
  87. url: opts.UserURL,
  88. accountType: "user",
  89. }
  90. } else if opts.OrgURL != "" {
  91. _, ownerName := path.Split(opts.OrgURL)
  92. owner = Owner{
  93. name: ownerName,
  94. url: opts.OrgURL,
  95. accountType: "org",
  96. }
  97. }
  98. if opts.Tmp {
  99. dir, err := ioutil.TempDir("", owner.name)
  100. if err != nil {
  101. log.Fatal("Cant make temp dir")
  102. }
  103. owner.path = dir
  104. } else {
  105. owner.path = filepath.Join(gitLeaksClonePath, owner.name)
  106. if _, err := os.Stat(owner.path); os.IsNotExist(err) {
  107. os.Mkdir(owner.path, os.ModePerm)
  108. }
  109. }
  110. owner.reportPath = filepath.Join(gitLeaksPath, "report", owner.name)
  111. return &owner
  112. }
  113. // getRepos
  114. func getRepos(opts *Options, owner *Owner) []RepoDesc {
  115. var (
  116. allRepos []*github.Repository
  117. repos []*github.Repository
  118. repoDescs []RepoDesc
  119. resp *github.Response
  120. ctx = context.Background()
  121. err error
  122. )
  123. if opts.RepoURL != "" {
  124. _, repoName := path.Split(opts.RepoURL)
  125. if strings.HasSuffix(repoName, ".git") {
  126. repoName = repoName[:len(repoName)-4]
  127. }
  128. ownerPath := filepath.Join(owner.path, repoName)
  129. repo := RepoDesc{
  130. name: repoName,
  131. url: opts.RepoURL,
  132. owner: owner,
  133. path: ownerPath}
  134. repoDescs = append(repoDescs, repo)
  135. return repoDescs
  136. }
  137. tokenClient := getAccessToken(opts)
  138. gitClient := github.NewClient(tokenClient)
  139. // TODO include fork check
  140. orgOpt := &github.RepositoryListByOrgOptions{
  141. ListOptions: github.ListOptions{PerPage: 10},
  142. }
  143. userOpt := &github.RepositoryListOptions{
  144. ListOptions: github.ListOptions{PerPage: 10},
  145. }
  146. for {
  147. if opts.UserURL != "" {
  148. repos, resp, err = gitClient.Repositories.List(
  149. ctx, owner.name, userOpt)
  150. } else if opts.OrgURL != "" {
  151. repos, resp, err = gitClient.Repositories.ListByOrg(
  152. ctx, owner.name, orgOpt)
  153. }
  154. allRepos = append(allRepos, repos...)
  155. if resp.NextPage == 0 || err != nil {
  156. break
  157. }
  158. for _, repo := range repos {
  159. repoPath := filepath.Join(owner.path, *repo.Name)
  160. repoDescs = append(repoDescs,
  161. RepoDesc{
  162. name: *repo.Name,
  163. url: *repo.CloneURL,
  164. owner: owner,
  165. path: repoPath})
  166. }
  167. orgOpt.Page = resp.NextPage
  168. userOpt.Page = resp.NextPage
  169. }
  170. return repoDescs
  171. }
  172. // getAccessToken checks
  173. // 1. option
  174. // 2. env var
  175. // TODO. $HOME/.gitleaks/.creds
  176. func getAccessToken(opts *Options) *http.Client {
  177. var token string
  178. if opts.Token != "" {
  179. token = opts.Token
  180. } else {
  181. token = os.Getenv("GITHUB_TOKEN")
  182. }
  183. if token == "" {
  184. return nil
  185. }
  186. tokenService := oauth2.StaticTokenSource(
  187. &oauth2.Token{AccessToken: token},
  188. )
  189. tokenClient := oauth2.NewClient(context.Background(), tokenService)
  190. return tokenClient
  191. }
  192. func main() {
  193. args := os.Args[1:]
  194. opts := parseOptions(args)
  195. owner := getOwner(opts)
  196. repos := getRepos(opts, owner)
  197. start(repos, owner, opts)
  198. }