github.go 4.2 KB

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