You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

hook_verification.go 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. // Package private includes all internal routes. The package name internal is ideal but Golang is not allowed, so we use private as package name instead.
  4. package private
  5. import (
  6. "bufio"
  7. "context"
  8. "fmt"
  9. "io"
  10. "os"
  11. asymkey_model "code.gitea.io/gitea/models/asymkey"
  12. "code.gitea.io/gitea/modules/git"
  13. "code.gitea.io/gitea/modules/log"
  14. )
  15. // _________ .__ __
  16. // \_ ___ \ ____ _____ _____ |__|/ |_
  17. // / \ \/ / _ \ / \ / \| \ __\
  18. // \ \___( <_> ) Y Y \ Y Y \ || |
  19. // \______ /\____/|__|_| /__|_| /__||__|
  20. // \/ \/ \/
  21. // ____ ____ .__ _____.__ __ .__
  22. // \ \ / /___________|__|/ ____\__| ____ _____ _/ |_|__| ____ ____
  23. // \ Y // __ \_ __ \ \ __\| |/ ___\\__ \\ __\ |/ _ \ / \
  24. // \ /\ ___/| | \/ || | | \ \___ / __ \| | | ( <_> ) | \
  25. // \___/ \___ >__| |__||__| |__|\___ >____ /__| |__|\____/|___| /
  26. // \/ \/ \/ \/
  27. //
  28. // This file contains commit verification functions for refs passed across in hooks
  29. func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []string) error {
  30. stdoutReader, stdoutWriter, err := os.Pipe()
  31. if err != nil {
  32. log.Error("Unable to create os.Pipe for %s", repo.Path)
  33. return err
  34. }
  35. defer func() {
  36. _ = stdoutReader.Close()
  37. _ = stdoutWriter.Close()
  38. }()
  39. // This is safe as force pushes are already forbidden
  40. err = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID).
  41. Run(&git.RunOpts{
  42. Env: env,
  43. Dir: repo.Path,
  44. Stdout: stdoutWriter,
  45. PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
  46. _ = stdoutWriter.Close()
  47. err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env)
  48. if err != nil {
  49. log.Error("%v", err)
  50. cancel()
  51. }
  52. _ = stdoutReader.Close()
  53. return err
  54. },
  55. })
  56. if err != nil && !isErrUnverifiedCommit(err) {
  57. log.Error("Unable to check commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err)
  58. }
  59. return err
  60. }
  61. func readAndVerifyCommitsFromShaReader(input io.ReadCloser, repo *git.Repository, env []string) error {
  62. scanner := bufio.NewScanner(input)
  63. for scanner.Scan() {
  64. line := scanner.Text()
  65. err := readAndVerifyCommit(line, repo, env)
  66. if err != nil {
  67. log.Error("%v", err)
  68. return err
  69. }
  70. }
  71. return scanner.Err()
  72. }
  73. func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error {
  74. stdoutReader, stdoutWriter, err := os.Pipe()
  75. if err != nil {
  76. log.Error("Unable to create pipe for %s: %v", repo.Path, err)
  77. return err
  78. }
  79. defer func() {
  80. _ = stdoutReader.Close()
  81. _ = stdoutWriter.Close()
  82. }()
  83. hash := git.MustIDFromString(sha)
  84. return git.NewCommand(repo.Ctx, "cat-file", "commit").AddDynamicArguments(sha).
  85. Run(&git.RunOpts{
  86. Env: env,
  87. Dir: repo.Path,
  88. Stdout: stdoutWriter,
  89. PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
  90. _ = stdoutWriter.Close()
  91. commit, err := git.CommitFromReader(repo, hash, stdoutReader)
  92. if err != nil {
  93. return err
  94. }
  95. verification := asymkey_model.ParseCommitWithSignature(commit)
  96. if !verification.Verified {
  97. cancel()
  98. return &errUnverifiedCommit{
  99. commit.ID.String(),
  100. }
  101. }
  102. return nil
  103. },
  104. })
  105. }
  106. type errUnverifiedCommit struct {
  107. sha string
  108. }
  109. func (e *errUnverifiedCommit) Error() string {
  110. return fmt.Sprintf("Unverified commit: %s", e.sha)
  111. }
  112. func isErrUnverifiedCommit(err error) bool {
  113. _, ok := err.(*errUnverifiedCommit)
  114. return ok
  115. }