github.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package hosts
  2. import (
  3. "context"
  4. "github.com/google/go-github/github"
  5. log "github.com/sirupsen/logrus"
  6. "github.com/zricethezav/gitleaks/audit"
  7. "github.com/zricethezav/gitleaks/manager"
  8. "github.com/zricethezav/gitleaks/options"
  9. "golang.org/x/oauth2"
  10. "gopkg.in/src-d/go-git.v4"
  11. "gopkg.in/src-d/go-git.v4/plumbing"
  12. "gopkg.in/src-d/go-git.v4/plumbing/object"
  13. "strconv"
  14. "strings"
  15. "sync"
  16. )
  17. // Github wraps a github client and manager. This struct implements what the Host interface defines.
  18. type Github struct {
  19. client *github.Client
  20. manager manager.Manager
  21. wg sync.WaitGroup
  22. }
  23. // NewGithubClient accepts a manager struct and returns a Github host pointer which will be used to
  24. // perform a github audit on an organization, user, or PR.
  25. func NewGithubClient(m manager.Manager) *Github {
  26. ctx := context.Background()
  27. token := oauth2.StaticTokenSource(
  28. &oauth2.Token{AccessToken: options.GetAccessToken(m.Opts)},
  29. )
  30. return &Github{
  31. manager: m,
  32. client: github.NewClient(oauth2.NewClient(ctx, token)),
  33. }
  34. }
  35. // Audit will audit a github user or organization's repos.
  36. func (g *Github) Audit() {
  37. ctx := context.Background()
  38. listOptions := github.ListOptions{
  39. PerPage: 100,
  40. Page: 1,
  41. }
  42. var githubRepos []*github.Repository
  43. for {
  44. var (
  45. _githubRepos []*github.Repository
  46. resp *github.Response
  47. err error
  48. )
  49. if g.manager.Opts.User != "" {
  50. _githubRepos, resp, err = g.client.Repositories.List(ctx, g.manager.Opts.User,
  51. &github.RepositoryListOptions{ListOptions: listOptions})
  52. } else if g.manager.Opts.Organization != "" {
  53. _githubRepos, resp, err = g.client.Repositories.ListByOrg(ctx, g.manager.Opts.Organization,
  54. &github.RepositoryListByOrgOptions{ListOptions: listOptions})
  55. }
  56. githubRepos = append(githubRepos, _githubRepos...)
  57. if resp == nil {
  58. break
  59. }
  60. if resp.LastPage != 0 {
  61. log.Infof("gathering github repos... progress: page %d of %d", listOptions.Page, resp.LastPage)
  62. } else {
  63. log.Infof("gathering github repos... progress: page %d of %d", listOptions.Page, listOptions.Page)
  64. }
  65. listOptions.Page = resp.NextPage
  66. if err != nil || listOptions.Page == 0 {
  67. break
  68. }
  69. }
  70. for _, repo := range githubRepos {
  71. r := audit.NewRepo(&g.manager)
  72. err := r.Clone(&git.CloneOptions{
  73. URL: *repo.CloneURL,
  74. })
  75. if err != nil {
  76. log.Warn("unable to clone via https and access token, attempting with ssh now")
  77. auth, err := options.SSHAuth(g.manager.Opts)
  78. if err != nil {
  79. log.Warnf("unable to get ssh auth, skipping clone and audit for repo %s: %+v\n", *repo.CloneURL, err)
  80. }
  81. err = r.Clone(&git.CloneOptions{
  82. URL: *repo.SSHURL,
  83. Auth: auth,
  84. })
  85. if err != nil {
  86. log.Warnf("err cloning %s, skipping clone and audit: %+v\n", *repo.SSHURL, err)
  87. }
  88. }
  89. if err = r.Audit(); err != nil {
  90. log.Warn(err)
  91. }
  92. }
  93. }
  94. // AuditPR audits a single github PR
  95. func (g *Github) AuditPR() {
  96. ctx := context.Background()
  97. splits := strings.Split(g.manager.Opts.PullRequest, "/")
  98. owner := splits[len(splits)-4]
  99. repoName := splits[len(splits)-3]
  100. prNum, err := strconv.Atoi(splits[len(splits)-1])
  101. repo := audit.NewRepo(&g.manager)
  102. repo.Name = repoName
  103. log.Infof("auditing pr %s\n", g.manager.Opts.PullRequest)
  104. if err != nil {
  105. return
  106. }
  107. page := 1
  108. for {
  109. commits, resp, err := g.client.PullRequests.ListCommits(ctx, owner, repoName, prNum, &github.ListOptions{
  110. PerPage: 100, Page: page})
  111. if err != nil {
  112. return
  113. }
  114. for _, c := range commits {
  115. c, _, err := g.client.Repositories.GetCommit(ctx, owner, repo.Name, *c.SHA)
  116. if err != nil {
  117. continue
  118. }
  119. commitObj := object.Commit{
  120. Hash: plumbing.NewHash(*c.SHA),
  121. Author: object.Signature{
  122. Name: *c.Commit.Author.Name,
  123. Email: *c.Commit.Author.Email,
  124. When: *c.Commit.Author.Date,
  125. },
  126. }
  127. for _, f := range c.Files {
  128. if f.Patch == nil {
  129. continue
  130. }
  131. audit.InspectString(*f.Patch, &commitObj, repo, *f.Filename)
  132. }
  133. }
  134. page = resp.NextPage
  135. if resp.LastPage == 0 {
  136. break
  137. }
  138. }
  139. }