Slack webhook channel name cannot be empty or just contain an hashtag (#4786)
This commit is contained in:
parent
6e03390aa8
commit
be48397945
6 changed files with 69 additions and 5 deletions
|
@ -10,6 +10,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/routers/utils"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/go-macaron/binding"
|
"github.com/go-macaron/binding"
|
||||||
"gopkg.in/macaron.v1"
|
"gopkg.in/macaron.v1"
|
||||||
|
@ -225,6 +227,11 @@ func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) b
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasInvalidChannel validates the channel name is in the right format
|
||||||
|
func (f NewSlackHookForm) HasInvalidChannel() bool {
|
||||||
|
return !utils.IsValidSlackChannel(f.Channel)
|
||||||
|
}
|
||||||
|
|
||||||
// NewDiscordHookForm form for creating discord hook
|
// NewDiscordHookForm form for creating discord hook
|
||||||
type NewDiscordHookForm struct {
|
type NewDiscordHookForm struct {
|
||||||
PayloadURL string `binding:"Required;ValidUrl"`
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
|
|
@ -1044,6 +1044,7 @@ settings.search_user_placeholder = Search user…
|
||||||
settings.org_not_allowed_to_be_collaborator = Organizations cannot be added as a collaborator.
|
settings.org_not_allowed_to_be_collaborator = Organizations cannot be added as a collaborator.
|
||||||
settings.user_is_org_member = The user is an organization member who cannot be added as a collaborator.
|
settings.user_is_org_member = The user is an organization member who cannot be added as a collaborator.
|
||||||
settings.add_webhook = Add Webhook
|
settings.add_webhook = Add Webhook
|
||||||
|
settings.add_webhook.invalid_channel_name = Webhook channel name cannot be empty and cannot contain only a # character.
|
||||||
settings.hooks_desc = Webhooks automatically make HTTP POST requests to a server when certain Gitea events trigger. Read more in the <a target="_blank" rel="noopener noreferrer" href="%s">webhooks guide</a>.
|
settings.hooks_desc = Webhooks automatically make HTTP POST requests to a server when certain Gitea events trigger. Read more in the <a target="_blank" rel="noopener noreferrer" href="%s">webhooks guide</a>.
|
||||||
settings.webhook_deletion = Remove Webhook
|
settings.webhook_deletion = Remove Webhook
|
||||||
settings.webhook_deletion_desc = Removing a webhook deletes its settings and delivery history. Continue?
|
settings.webhook_deletion_desc = Removing a webhook deletes its settings and delivery history. Continue?
|
||||||
|
|
|
@ -5,14 +5,16 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
api "code.gitea.io/sdk/gitea"
|
|
||||||
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/routers/api/v1/convert"
|
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||||
|
"code.gitea.io/gitea/routers/utils"
|
||||||
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -119,8 +121,14 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
|
||||||
ctx.Error(422, "", "Missing config option: channel")
|
ctx.Error(422, "", "Missing config option: channel")
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !utils.IsValidSlackChannel(channel) {
|
||||||
|
ctx.Error(400, "", "Invalid slack channel name")
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
meta, err := json.Marshal(&models.SlackMeta{
|
meta, err := json.Marshal(&models.SlackMeta{
|
||||||
Channel: channel,
|
Channel: strings.TrimSpace(channel),
|
||||||
Username: form.Config["username"],
|
Username: form.Config["username"],
|
||||||
IconURL: form.Config["icon_url"],
|
IconURL: form.Config["icon_url"],
|
||||||
Color: form.Config["color"],
|
Color: form.Config["color"],
|
||||||
|
|
|
@ -332,8 +332,14 @@ func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.HasInvalidChannel() {
|
||||||
|
ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
|
||||||
|
ctx.Redirect(orCtx.Link + "/settings/hooks/slack/new")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
meta, err := json.Marshal(&models.SlackMeta{
|
meta, err := json.Marshal(&models.SlackMeta{
|
||||||
Channel: form.Channel,
|
Channel: strings.TrimSpace(form.Channel),
|
||||||
Username: form.Username,
|
Username: form.Username,
|
||||||
IconURL: form.IconURL,
|
IconURL: form.IconURL,
|
||||||
Color: form.Color,
|
Color: form.Color,
|
||||||
|
@ -515,8 +521,14 @@ func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.HasInvalidChannel() {
|
||||||
|
ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
|
||||||
|
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
meta, err := json.Marshal(&models.SlackMeta{
|
meta, err := json.Marshal(&models.SlackMeta{
|
||||||
Channel: form.Channel,
|
Channel: strings.TrimSpace(form.Channel),
|
||||||
Username: form.Username,
|
Username: form.Username,
|
||||||
IconURL: form.IconURL,
|
IconURL: form.IconURL,
|
||||||
Color: form.Color,
|
Color: form.Color,
|
||||||
|
|
|
@ -15,3 +15,22 @@ func RemoveUsernameParameterSuffix(name string) string {
|
||||||
}
|
}
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsValidSlackChannel validates a channel name conforms to what slack expects.
|
||||||
|
// It makes sure a channel name cannot be empty and invalid ( only an # )
|
||||||
|
func IsValidSlackChannel(channelName string) bool {
|
||||||
|
switch len(strings.TrimSpace(channelName)) {
|
||||||
|
case 0:
|
||||||
|
return false
|
||||||
|
case 1:
|
||||||
|
// Keep default behaviour where a channel name is still
|
||||||
|
// valid without an #
|
||||||
|
// But if it contains only an #, it should be regarded as
|
||||||
|
// invalid
|
||||||
|
if channelName[0] == '#' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -15,3 +15,20 @@ func TestRemoveUsernameParameterSuffix(t *testing.T) {
|
||||||
assert.Equal(t, "foobar", RemoveUsernameParameterSuffix("foobar"))
|
assert.Equal(t, "foobar", RemoveUsernameParameterSuffix("foobar"))
|
||||||
assert.Equal(t, "", RemoveUsernameParameterSuffix(""))
|
assert.Equal(t, "", RemoveUsernameParameterSuffix(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsValidSlackChannel(t *testing.T) {
|
||||||
|
tt := []struct {
|
||||||
|
channelName string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"gitea", true},
|
||||||
|
{" ", false},
|
||||||
|
{"#", false},
|
||||||
|
{"gitea ", true},
|
||||||
|
{" gitea", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range tt {
|
||||||
|
assert.Equal(t, v.expected, IsValidSlackChannel(v.channelName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in a new issue