aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2025-05-17 13:05:55 -0700
committerGitHub <noreply@github.com>2025-05-17 20:05:55 +0000
commit972381097c6b3488bf9d5c3c4fde04775b2e3a3c (patch)
tree26d51417d7e3ef058d0bc809afd556da7a78b65e
parentb6c066747400927407a6b4807165d2de40c7495b (diff)
downloadgitea-972381097c6b3488bf9d5c3c4fde04775b2e3a3c.tar.gz
gitea-972381097c6b3488bf9d5c3c4fde04775b2e3a3c.zip
Fix url validation in webhook add/edit API (#34492)
-rw-r--r--routers/api/v1/utils/hook.go9
-rw-r--r--routers/api/v1/utils/hook_test.go82
-rw-r--r--routers/api/v1/utils/main_test.go21
3 files changed, 112 insertions, 0 deletions
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index 532d157e35..b6e650bdc0 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/validation"
webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/services/context"
webhook_service "code.gitea.io/gitea/services/webhook"
@@ -92,6 +93,10 @@ func checkCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption)
ctx.APIError(http.StatusUnprocessableEntity, "Invalid content type")
return false
}
+ if !validation.IsValidURL(form.Config["url"]) {
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid url")
+ return false
+ }
return true
}
@@ -324,6 +329,10 @@ func EditRepoHook(ctx *context.APIContext, form *api.EditHookOption, hookID int6
func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webhook) bool {
if form.Config != nil {
if url, ok := form.Config["url"]; ok {
+ if !validation.IsValidURL(url) {
+ ctx.APIError(http.StatusUnprocessableEntity, "Invalid url")
+ return false
+ }
w.URL = url
}
if ct, ok := form.Config["content_type"]; ok {
diff --git a/routers/api/v1/utils/hook_test.go b/routers/api/v1/utils/hook_test.go
new file mode 100644
index 0000000000..e5e8ce07ce
--- /dev/null
+++ b/routers/api/v1/utils/hook_test.go
@@ -0,0 +1,82 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "net/http"
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/contexttest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTestHookValidation(t *testing.T) {
+ unittest.PrepareTestEnv(t)
+
+ t.Run("Test Validation", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "gitea",
+ Config: map[string]string{
+ "content_type": "json",
+ "url": "https://example.com/webhook",
+ },
+ })
+ assert.Equal(t, 0, ctx.Resp.WrittenStatus()) // not written yet
+ })
+
+ t.Run("Test Validation with invalid URL", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "gitea",
+ Config: map[string]string{
+ "content_type": "json",
+ "url": "example.com/webhook",
+ },
+ })
+ assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
+ })
+
+ t.Run("Test Validation with invalid webhook type", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "unknown",
+ Config: map[string]string{
+ "content_type": "json",
+ "url": "example.com/webhook",
+ },
+ })
+ assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
+ })
+
+ t.Run("Test Validation with empty content type", func(t *testing.T) {
+ ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+
+ checkCreateHookOption(ctx, &structs.CreateHookOption{
+ Type: "unknown",
+ Config: map[string]string{
+ "url": "https://example.com/webhook",
+ },
+ })
+ assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
+ })
+}
diff --git a/routers/api/v1/utils/main_test.go b/routers/api/v1/utils/main_test.go
new file mode 100644
index 0000000000..4eace1f369
--- /dev/null
+++ b/routers/api/v1/utils/main_test.go
@@ -0,0 +1,21 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package utils
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/setting"
+ webhook_service "code.gitea.io/gitea/services/webhook"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m, &unittest.TestOptions{
+ SetUp: func() error {
+ setting.LoadQueueSettings()
+ return webhook_service.Init()
+ },
+ })
+}