Skip to content

Commit 9723810

Browse files
authored
Fix url validation in webhook add/edit API (#34492)
1 parent b6c0667 commit 9723810

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed

routers/api/v1/utils/hook.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"code.gitea.io/gitea/modules/setting"
1616
api "code.gitea.io/gitea/modules/structs"
1717
"code.gitea.io/gitea/modules/util"
18+
"code.gitea.io/gitea/modules/validation"
1819
webhook_module "code.gitea.io/gitea/modules/webhook"
1920
"code.gitea.io/gitea/services/context"
2021
webhook_service "code.gitea.io/gitea/services/webhook"
@@ -92,6 +93,10 @@ func checkCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption)
9293
ctx.APIError(http.StatusUnprocessableEntity, "Invalid content type")
9394
return false
9495
}
96+
if !validation.IsValidURL(form.Config["url"]) {
97+
ctx.APIError(http.StatusUnprocessableEntity, "Invalid url")
98+
return false
99+
}
95100
return true
96101
}
97102

@@ -324,6 +329,10 @@ func EditRepoHook(ctx *context.APIContext, form *api.EditHookOption, hookID int6
324329
func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webhook) bool {
325330
if form.Config != nil {
326331
if url, ok := form.Config["url"]; ok {
332+
if !validation.IsValidURL(url) {
333+
ctx.APIError(http.StatusUnprocessableEntity, "Invalid url")
334+
return false
335+
}
327336
w.URL = url
328337
}
329338
if ct, ok := form.Config["content_type"]; ok {

routers/api/v1/utils/hook_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package utils
5+
6+
import (
7+
"net/http"
8+
"testing"
9+
10+
"code.gitea.io/gitea/models/unittest"
11+
"code.gitea.io/gitea/modules/structs"
12+
"code.gitea.io/gitea/services/contexttest"
13+
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
func TestTestHookValidation(t *testing.T) {
18+
unittest.PrepareTestEnv(t)
19+
20+
t.Run("Test Validation", func(t *testing.T) {
21+
ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
22+
contexttest.LoadRepo(t, ctx, 1)
23+
contexttest.LoadRepoCommit(t, ctx)
24+
contexttest.LoadUser(t, ctx, 2)
25+
26+
checkCreateHookOption(ctx, &structs.CreateHookOption{
27+
Type: "gitea",
28+
Config: map[string]string{
29+
"content_type": "json",
30+
"url": "https://example.com/webhook",
31+
},
32+
})
33+
assert.Equal(t, 0, ctx.Resp.WrittenStatus()) // not written yet
34+
})
35+
36+
t.Run("Test Validation with invalid URL", func(t *testing.T) {
37+
ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
38+
contexttest.LoadRepo(t, ctx, 1)
39+
contexttest.LoadRepoCommit(t, ctx)
40+
contexttest.LoadUser(t, ctx, 2)
41+
42+
checkCreateHookOption(ctx, &structs.CreateHookOption{
43+
Type: "gitea",
44+
Config: map[string]string{
45+
"content_type": "json",
46+
"url": "example.com/webhook",
47+
},
48+
})
49+
assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
50+
})
51+
52+
t.Run("Test Validation with invalid webhook type", func(t *testing.T) {
53+
ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
54+
contexttest.LoadRepo(t, ctx, 1)
55+
contexttest.LoadRepoCommit(t, ctx)
56+
contexttest.LoadUser(t, ctx, 2)
57+
58+
checkCreateHookOption(ctx, &structs.CreateHookOption{
59+
Type: "unknown",
60+
Config: map[string]string{
61+
"content_type": "json",
62+
"url": "example.com/webhook",
63+
},
64+
})
65+
assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
66+
})
67+
68+
t.Run("Test Validation with empty content type", func(t *testing.T) {
69+
ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/hooks")
70+
contexttest.LoadRepo(t, ctx, 1)
71+
contexttest.LoadRepoCommit(t, ctx)
72+
contexttest.LoadUser(t, ctx, 2)
73+
74+
checkCreateHookOption(ctx, &structs.CreateHookOption{
75+
Type: "unknown",
76+
Config: map[string]string{
77+
"url": "https://example.com/webhook",
78+
},
79+
})
80+
assert.Equal(t, http.StatusUnprocessableEntity, ctx.Resp.WrittenStatus())
81+
})
82+
}

routers/api/v1/utils/main_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2018 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package utils
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/unittest"
10+
"code.gitea.io/gitea/modules/setting"
11+
webhook_service "code.gitea.io/gitea/services/webhook"
12+
)
13+
14+
func TestMain(m *testing.M) {
15+
unittest.MainTest(m, &unittest.TestOptions{
16+
SetUp: func() error {
17+
setting.LoadQueueSettings()
18+
return webhook_service.Init()
19+
},
20+
})
21+
}

0 commit comments

Comments
 (0)