gitea源码

hook_verification.go 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package private
  4. import (
  5. "bufio"
  6. "context"
  7. "io"
  8. "os"
  9. "code.gitea.io/gitea/modules/git"
  10. "code.gitea.io/gitea/modules/git/gitcmd"
  11. "code.gitea.io/gitea/modules/log"
  12. asymkey_service "code.gitea.io/gitea/services/asymkey"
  13. )
  14. // This file contains commit verification functions for refs passed across in hooks
  15. func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []string) error {
  16. stdoutReader, stdoutWriter, err := os.Pipe()
  17. if err != nil {
  18. log.Error("Unable to create os.Pipe for %s", repo.Path)
  19. return err
  20. }
  21. defer func() {
  22. _ = stdoutReader.Close()
  23. _ = stdoutWriter.Close()
  24. }()
  25. var command *gitcmd.Command
  26. objectFormat, _ := repo.GetObjectFormat()
  27. if oldCommitID == objectFormat.EmptyObjectID().String() {
  28. // When creating a new branch, the oldCommitID is empty, by using "newCommitID --not --all":
  29. // List commits that are reachable by following the newCommitID, exclude "all" existing heads/tags commits
  30. // So, it only lists the new commits received, doesn't list the commits already present in the receiving repository
  31. command = gitcmd.NewCommand("rev-list").AddDynamicArguments(newCommitID).AddArguments("--not", "--all")
  32. } else {
  33. command = gitcmd.NewCommand("rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID)
  34. }
  35. // This is safe as force pushes are already forbidden
  36. err = command.Run(repo.Ctx, &gitcmd.RunOpts{
  37. Env: env,
  38. Dir: repo.Path,
  39. Stdout: stdoutWriter,
  40. PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
  41. _ = stdoutWriter.Close()
  42. err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env)
  43. if err != nil {
  44. log.Error("readAndVerifyCommitsFromShaReader failed: %v", err)
  45. cancel()
  46. }
  47. _ = stdoutReader.Close()
  48. return err
  49. },
  50. })
  51. if err != nil && !isErrUnverifiedCommit(err) {
  52. log.Error("Unable to check commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err)
  53. }
  54. return err
  55. }
  56. func readAndVerifyCommitsFromShaReader(input io.ReadCloser, repo *git.Repository, env []string) error {
  57. scanner := bufio.NewScanner(input)
  58. for scanner.Scan() {
  59. line := scanner.Text()
  60. err := readAndVerifyCommit(line, repo, env)
  61. if err != nil {
  62. return err
  63. }
  64. }
  65. return scanner.Err()
  66. }
  67. func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error {
  68. stdoutReader, stdoutWriter, err := os.Pipe()
  69. if err != nil {
  70. log.Error("Unable to create pipe for %s: %v", repo.Path, err)
  71. return err
  72. }
  73. defer func() {
  74. _ = stdoutReader.Close()
  75. _ = stdoutWriter.Close()
  76. }()
  77. commitID := git.MustIDFromString(sha)
  78. return gitcmd.NewCommand("cat-file", "commit").AddDynamicArguments(sha).
  79. Run(repo.Ctx, &gitcmd.RunOpts{
  80. Env: env,
  81. Dir: repo.Path,
  82. Stdout: stdoutWriter,
  83. PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
  84. _ = stdoutWriter.Close()
  85. commit, err := git.CommitFromReader(repo, commitID, stdoutReader)
  86. if err != nil {
  87. return err
  88. }
  89. verification := asymkey_service.ParseCommitWithSignature(ctx, commit)
  90. if !verification.Verified {
  91. cancel()
  92. return &errUnverifiedCommit{
  93. commit.ID.String(),
  94. }
  95. }
  96. return nil
  97. },
  98. })
  99. }
  100. type errUnverifiedCommit struct {
  101. sha string
  102. }
  103. func (e *errUnverifiedCommit) Error() string {
  104. return "Unverified commit: " + e.sha
  105. }
  106. func isErrUnverifiedCommit(err error) bool {
  107. _, ok := err.(*errUnverifiedCommit)
  108. return ok
  109. }