github.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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 {
  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. r.Name = *repo.Name
  73. err := r.Clone(&git.CloneOptions{
  74. URL: *repo.CloneURL,
  75. })
  76. if err != nil {
  77. log.Warn("unable to clone via https and access token, attempting with ssh now")
  78. auth, err := options.SSHAuth(g.manager.Opts)
  79. if err != nil {
  80. log.Warnf("unable to get ssh auth, skipping clone and audit for repo %s: %+v\n", *repo.CloneURL, err)
  81. }
  82. err = r.Clone(&git.CloneOptions{
  83. URL: *repo.SSHURL,
  84. Auth: auth,
  85. })
  86. if err != nil {
  87. log.Warnf("err cloning %s, skipping clone and audit: %+v\n", *repo.SSHURL, err)
  88. }
  89. }
  90. if err = r.Audit(); err != nil {
  91. log.Warn(err)
  92. }
  93. }
  94. }
  95. // AuditPR audits a single github PR
  96. func (g *Github) AuditPR() {
  97. ctx := context.Background()
  98. splits := strings.Split(g.manager.Opts.PullRequest, "/")
  99. owner := splits[len(splits)-4]
  100. repoName := splits[len(splits)-3]
  101. prNum, err := strconv.Atoi(splits[len(splits)-1])
  102. repo := audit.NewRepo(&g.manager)
  103. repo.Name = repoName
  104. log.Infof("auditing pr %s\n", g.manager.Opts.PullRequest)
  105. if err != nil {
  106. return
  107. }
  108. page := 1
  109. for {
  110. commits, resp, err := g.client.PullRequests.ListCommits(ctx, owner, repoName, prNum, &github.ListOptions{
  111. PerPage: 100, Page: page})
  112. if err != nil {
  113. return
  114. }
  115. for _, c := range commits {
  116. c, _, err := g.client.Repositories.GetCommit(ctx, owner, repo.Name, *c.SHA)
  117. if err != nil {
  118. continue
  119. }
  120. commitObj := object.Commit{
  121. Hash: plumbing.NewHash(*c.SHA),
  122. Author: object.Signature{
  123. Name: *c.Commit.Author.Name,
  124. Email: *c.Commit.Author.Email,
  125. When: *c.Commit.Author.Date,
  126. },
  127. }
  128. for _, f := range c.Files {
  129. if f.Patch == nil {
  130. continue
  131. }
  132. audit.InspectString(*f.Patch, &commitObj, repo, *f.Filename)
  133. }
  134. }
  135. page = resp.NextPage
  136. if resp.LastPage == 0 {
  137. break
  138. }
  139. }
  140. }