4
0

git.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package git
  2. import (
  3. "bufio"
  4. "io"
  5. "os/exec"
  6. "path/filepath"
  7. "strings"
  8. "time"
  9. "github.com/gitleaks/go-gitdiff/gitdiff"
  10. "github.com/rs/zerolog/log"
  11. )
  12. var ErrEncountered bool
  13. // GitLog returns a channel of gitdiff.File objects from the
  14. // git log -p command for the given source.
  15. func GitLog(source string, logOpts string) (<-chan *gitdiff.File, error) {
  16. sourceClean := filepath.Clean(source)
  17. var cmd *exec.Cmd
  18. if logOpts != "" {
  19. args := []string{"-C", sourceClean, "log", "-p", "-U0"}
  20. args = append(args, strings.Split(logOpts, " ")...)
  21. cmd = exec.Command("git", args...)
  22. } else {
  23. cmd = exec.Command("git", "-C", sourceClean, "log", "-p", "-U0",
  24. "--full-history", "--all")
  25. }
  26. log.Debug().Msgf("executing: %s", cmd.String())
  27. stdout, err := cmd.StdoutPipe()
  28. if err != nil {
  29. return nil, err
  30. }
  31. stderr, err := cmd.StderrPipe()
  32. if err != nil {
  33. return nil, err
  34. }
  35. go listenForStdErr(stderr)
  36. if err := cmd.Start(); err != nil {
  37. return nil, err
  38. }
  39. // HACK: to avoid https://github.com/zricethezav/gitleaks/issues/722
  40. time.Sleep(50 * time.Millisecond)
  41. return gitdiff.Parse(cmd, stdout)
  42. }
  43. // GitDiff returns a channel of gitdiff.File objects from
  44. // the git diff command for the given source.
  45. func GitDiff(source string, staged bool) (<-chan *gitdiff.File, error) {
  46. sourceClean := filepath.Clean(source)
  47. var cmd *exec.Cmd
  48. cmd = exec.Command("git", "-C", sourceClean, "diff", "-U0", ".")
  49. if staged {
  50. cmd = exec.Command("git", "-C", sourceClean, "diff", "-U0",
  51. "--staged", ".")
  52. }
  53. log.Debug().Msgf("executing: %s", cmd.String())
  54. stdout, err := cmd.StdoutPipe()
  55. if err != nil {
  56. return nil, err
  57. }
  58. stderr, err := cmd.StderrPipe()
  59. if err != nil {
  60. return nil, err
  61. }
  62. go listenForStdErr(stderr)
  63. if err := cmd.Start(); err != nil {
  64. return nil, err
  65. }
  66. // HACK: to avoid https://github.com/zricethezav/gitleaks/issues/722
  67. time.Sleep(50 * time.Millisecond)
  68. return gitdiff.Parse(cmd, stdout)
  69. }
  70. // listenForStdErr listens for stderr output from git and prints it to stdout
  71. // then exits with exit code 1
  72. func listenForStdErr(stderr io.ReadCloser) {
  73. scanner := bufio.NewScanner(stderr)
  74. for scanner.Scan() {
  75. // if git throws one of the following errors:
  76. //
  77. // exhaustive rename detection was skipped due to too many files.
  78. // you may want to set your diff.renameLimit variable to at least
  79. // (some large number) and retry the command.
  80. //
  81. // inexact rename detection was skipped due to too many files.
  82. // you may want to set your diff.renameLimit variable to at least
  83. // (some large number) and retry the command.
  84. //
  85. // we skip exiting the program as git log -p/git diff will continue
  86. // to send data to stdout and finish executing. This next bit of
  87. // code prevents gitleaks from stopping mid scan if this error is
  88. // encountered
  89. if strings.Contains(scanner.Text(),
  90. "exhaustive rename detection was skipped") ||
  91. strings.Contains(scanner.Text(),
  92. "inexact rename detection was skipped") ||
  93. strings.Contains(scanner.Text(),
  94. "you may want to set your diff.renameLimit") {
  95. log.Warn().Msg(scanner.Text())
  96. } else {
  97. log.Error().Msgf("[git] %s", scanner.Text())
  98. // asynchronously set this error flag to true so that we can
  99. // capture a log message and exit with a non-zero exit code
  100. // This value should get set before the `git` command exits so it's
  101. // safe-ish, although I know I know, bad practice.
  102. ErrEncountered = true
  103. }
  104. }
  105. }