]> source.dussan.org Git - gitea.git/commitdiff
Prevent update pull refs manually and will not affect other refs update (#31931)
authorLunny Xiao <xiaolunwen@gmail.com>
Mon, 2 Sep 2024 07:38:27 +0000 (15:38 +0800)
committerGitHub <noreply@github.com>
Mon, 2 Sep 2024 07:38:27 +0000 (07:38 +0000)
All refs under `refs/pull` should only be changed from Gitea inside but
not by pushing from outside of Gitea.
This PR will prevent the pull refs update but allow other refs to be
updated on the same pushing with `--mirror` operations.

The main changes are to add checks on `update` hook but not
`pre-receive` because `update` will be invoked by every ref but
`pre-receive` will revert all changes once one ref update fails.

cmd/hook.go
tests/integration/git_push_test.go
tests/test_utils.go

index 6e31710caff2180e5409ebaf42b7546e3cc146ef..e8a8b1f4ad247b4c5919c71f6115866d3ddf25a6 100644 (file)
@@ -290,8 +290,22 @@ Gitea or set your environment appropriately.`, "")
        return nil
 }
 
+// runHookUpdate avoid to do heavy operations on update hook because it will be
+// invoked for every ref update which does not like pre-receive and post-receive
 func runHookUpdate(c *cli.Context) error {
+       if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
+               return nil
+       }
+
        // Update is empty and is kept only for backwards compatibility
+       if len(os.Args) < 3 {
+               return nil
+       }
+       refName := git.RefName(os.Args[len(os.Args)-3])
+       if refName.IsPull() {
+               // ignore update to refs/pull/xxx/head, so we don't need to output any information
+               os.Exit(1)
+       }
        return nil
 }
 
index da254fc88f6536b610e546b2e6a6e9adb813ab7b..dc0b52203a9a51fe73700bb5c641ec1017a967c2 100644 (file)
@@ -6,8 +6,10 @@ package integration
 import (
        "fmt"
        "net/url"
+       "strings"
        "testing"
 
+       auth_model "code.gitea.io/gitea/models/auth"
        "code.gitea.io/gitea/models/db"
        git_model "code.gitea.io/gitea/models/git"
        "code.gitea.io/gitea/models/unittest"
@@ -192,3 +194,23 @@ func runTestGitPush(t *testing.T, u *url.URL, gitOperation func(t *testing.T, gi
 
        require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID))
 }
+
+func TestPushPullRefs(t *testing.T) {
+       onGiteaRun(t, func(t *testing.T, u *url.URL) {
+               baseAPITestContext := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+               u.Path = baseAPITestContext.GitPath()
+               u.User = url.UserPassword("user2", userPassword)
+
+               dstPath := t.TempDir()
+               doGitClone(dstPath, u)(t)
+
+               cmd := git.NewCommand(git.DefaultContext, "push", "--delete", "origin", "refs/pull/2/head")
+               stdout, stderr, err := cmd.RunStdString(&git.RunOpts{
+                       Dir: dstPath,
+               })
+               assert.Error(t, err)
+               assert.Empty(t, stdout)
+               assert.False(t, strings.Contains(stderr, "[deleted]"), "stderr: %s", stderr)
+       })
+}
index 66a287ecad2622253dacfa141b43cc92f42a0472..6f9592b204112a38f98b6d49aad00529f1829f2a 100644 (file)
@@ -223,6 +223,7 @@ func PrepareTestEnv(t testing.TB, skip ...int) func() {
                        _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0o755)
                        _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0o755)
                        _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0o755)
+                       _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "pull"), 0o755)
                }
        }