repo.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "os"
  9. "os/exec"
  10. "strings"
  11. "sync"
  12. )
  13. type ReportElem struct {
  14. Lines []string `json:"lines"`
  15. Branch string `json:"branch"`
  16. CommitA string `json:"commitA"`
  17. CommitB string `json:"commitB"`
  18. }
  19. type Repo struct {
  20. url string
  21. name string
  22. path string
  23. }
  24. type MemoMessage struct {
  25. hash string
  26. leaks []string
  27. commitA string
  28. commitB string
  29. branch string
  30. }
  31. var lock = sync.RWMutex{}
  32. var cmdLock = sync.Mutex{}
  33. func repoStart(repoUrl string) {
  34. err := exec.Command("git", "clone", repoUrl).Run()
  35. if err != nil {
  36. log.Fatalf("failed to clone repo %v", err)
  37. }
  38. repoName := strings.Split(repoUrl, "/")[4]
  39. if err := os.Chdir(repoName); err != nil {
  40. log.Fatal(err)
  41. }
  42. repo := Repo{repoUrl, repoName, ""}
  43. report := repo.audit()
  44. repo.cleanup()
  45. reportJson, _ := json.MarshalIndent(report, "", "\t")
  46. err = ioutil.WriteFile(fmt.Sprintf("%s_leaks.json", repo.name), reportJson, 0644)
  47. }
  48. // cleanup changes to app root and recursive rms target repo
  49. func (repo Repo) cleanup() {
  50. if err := os.Chdir(appRoot); err != nil {
  51. log.Fatalf("failed cleaning up repo. Does the repo exist? %v", err)
  52. }
  53. err := exec.Command("rm", "-rf", repo.name).Run()
  54. if err != nil {
  55. log.Fatal(err)
  56. }
  57. }
  58. // audit parses git branch --all
  59. func (repo Repo) audit() []ReportElem {
  60. var (
  61. out []byte
  62. err error
  63. branch string
  64. commits [][]byte
  65. // leaks []string
  66. wg sync.WaitGroup
  67. commitA string
  68. commitB string
  69. concurrent = 10
  70. semaphoreChan = make(chan struct{}, concurrent)
  71. )
  72. out, err = exec.Command("git", "branch", "--all").Output()
  73. if err != nil {
  74. log.Fatalf("error retrieving branches %v\n", err)
  75. }
  76. // iterate through branches, git rev-list <branch>
  77. branches := bytes.Split(out, []byte("\n"))
  78. messages := make(chan MemoMessage)
  79. for i, branchB := range branches {
  80. if i < 2 || i == len(branches)-1 {
  81. continue
  82. }
  83. branch = string(bytes.Trim(branchB, " "))
  84. cmdLock.Lock()
  85. cmd := exec.Command("git", "rev-list", branch)
  86. out, err := cmd.Output()
  87. cmdLock.Unlock()
  88. if err != nil {
  89. fmt.Println("skipping branch", branch, err)
  90. continue
  91. }
  92. // iterate through commits
  93. commits = bytes.Split(out, []byte("\n"))
  94. for j, currCommit := range commits {
  95. if j == len(commits)-2 {
  96. break
  97. }
  98. commitA = string(commits[j+1])
  99. commitB = string(currCommit)
  100. _, seen := cache[commitA+commitB]
  101. if seen {
  102. fmt.Println("WE HAVE SEEN THIS")
  103. continue
  104. } else {
  105. cache[commitA+commitB] = true
  106. }
  107. wg.Add(1)
  108. go func(commitA string, commitB string,
  109. j int, branch string) {
  110. defer wg.Done()
  111. var leakPrs bool
  112. var leaks []string
  113. fmt.Println(j, branch)
  114. if err := os.Chdir(fmt.Sprintf("%s/%s", appRoot, repo.name)); err != nil {
  115. log.Fatal(err)
  116. }
  117. semaphoreChan <- struct{}{}
  118. fmt.Println("diffing", branch, j)
  119. cmd := exec.Command("git", "diff", commitA, commitB)
  120. out, err := cmd.Output()
  121. <-semaphoreChan
  122. if err != nil {
  123. fmt.Println("no diff: ", err)
  124. return
  125. }
  126. lines := checkRegex(string(out))
  127. if len(lines) == 0 {
  128. return
  129. }
  130. for _, line := range lines {
  131. leakPrs = checkEntropy(line)
  132. if leakPrs {
  133. leaks = append(leaks, line)
  134. }
  135. }
  136. messages <- MemoMessage{commitA + commitB, leaks, commitA, commitB, branch}
  137. }(commitA, commitB, j, branch)
  138. }
  139. }
  140. go func() {
  141. for memoMsg := range messages {
  142. fmt.Println(memoMsg)
  143. if len(memoMsg.leaks) != 0 {
  144. report = append(report, ReportElem{memoMsg.leaks, memoMsg.branch,
  145. memoMsg.commitA, memoMsg.commitB})
  146. }
  147. }
  148. }()
  149. wg.Wait()
  150. return report
  151. }