Skip to content

Commit 47d69b7

Browse files
kemzeblunnywxiaoguang
authored
Validate hex colors when creating/editing labels (#34623)
Resolves #34618. --------- Co-authored-by: Lunny Xiao <[email protected]> Co-authored-by: wxiaoguang <[email protected]>
1 parent b38f2d3 commit 47d69b7

File tree

9 files changed

+150
-75
lines changed

9 files changed

+150
-75
lines changed

modules/label/label.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import (
77
"fmt"
88
"regexp"
99
"strings"
10-
)
10+
"sync"
1111

12-
// colorPattern is a regexp which can validate label color
13-
var colorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$")
12+
"code.gitea.io/gitea/modules/util"
13+
)
1414

1515
// Label represents label information loaded from template
1616
type Label struct {
@@ -21,6 +21,10 @@ type Label struct {
2121
ExclusiveOrder int `yaml:"exclusive_order,omitempty"`
2222
}
2323

24+
var colorPattern = sync.OnceValue(func() *regexp.Regexp {
25+
return regexp.MustCompile(`^#([\da-fA-F]{3}|[\da-fA-F]{6})$`)
26+
})
27+
2428
// NormalizeColor normalizes a color string to a 6-character hex code
2529
func NormalizeColor(color string) (string, error) {
2630
// normalize case
@@ -31,8 +35,8 @@ func NormalizeColor(color string) (string, error) {
3135
color = "#" + color
3236
}
3337

34-
if !colorPattern.MatchString(color) {
35-
return "", fmt.Errorf("bad color code: %s", color)
38+
if !colorPattern().MatchString(color) {
39+
return "", util.NewInvalidArgumentErrorf("invalid color: %s", color)
3640
}
3741

3842
// convert 3-character shorthand into 6-character version

modules/test/utils.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
// RedirectURL returns the redirect URL of a http response.
1919
// It also works for JSONRedirect: `{"redirect": "..."}`
20+
// FIXME: it should separate the logic of checking from header and JSON body
2021
func RedirectURL(resp http.ResponseWriter) string {
2122
loc := resp.Header().Get("Location")
2223
if loc != "" {
@@ -34,6 +35,15 @@ func RedirectURL(resp http.ResponseWriter) string {
3435
return ""
3536
}
3637

38+
func ParseJSONError(buf []byte) (ret struct {
39+
ErrorMessage string `json:"errorMessage"`
40+
RenderFormat string `json:"renderFormat"`
41+
},
42+
) {
43+
_ = json.Unmarshal(buf, &ret)
44+
return ret
45+
}
46+
3747
func IsNormalPageCompleted(s string) bool {
3848
return strings.Contains(s, `<footer class="page-footer"`) && strings.Contains(s, `</html>`)
3949
}

options/locale/locale_en-US.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1433,7 +1433,6 @@ commitstatus.success = Success
14331433
ext_issues = Access to External Issues
14341434
ext_issues.desc = Link to an external issue tracker.
14351435
1436-
projects = Projects
14371436
projects.desc = Manage issues and pulls in projects.
14381437
projects.description = Description (optional)
14391438
projects.description_placeholder = Description
@@ -1653,6 +1652,7 @@ issues.save = Save
16531652
issues.label_title = Name
16541653
issues.label_description = Description
16551654
issues.label_color = Color
1655+
issues.label_color_invalid = Invalid color
16561656
issues.label_exclusive = Exclusive
16571657
issues.label_archive = Archive Label
16581658
issues.label_archived_filter = Show archived labels

routers/web/org/org_labels.go

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
package org
55

66
import (
7-
"net/http"
7+
"errors"
88

99
"code.gitea.io/gitea/models/db"
1010
issues_model "code.gitea.io/gitea/models/issues"
1111
"code.gitea.io/gitea/modules/label"
1212
repo_module "code.gitea.io/gitea/modules/repository"
13+
"code.gitea.io/gitea/modules/util"
1314
"code.gitea.io/gitea/modules/web"
15+
shared_label "code.gitea.io/gitea/routers/web/shared/label"
1416
"code.gitea.io/gitea/services/context"
1517
"code.gitea.io/gitea/services/forms"
1618
)
@@ -32,14 +34,8 @@ func RetrieveLabels(ctx *context.Context) {
3234

3335
// NewLabel create new label for organization
3436
func NewLabel(ctx *context.Context) {
35-
form := web.GetForm(ctx).(*forms.CreateLabelForm)
36-
ctx.Data["Title"] = ctx.Tr("repo.labels")
37-
ctx.Data["PageIsLabels"] = true
38-
ctx.Data["PageIsOrgSettings"] = true
39-
40-
if ctx.HasError() {
41-
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
42-
ctx.Redirect(ctx.Org.OrgLink + "/settings/labels")
37+
form := shared_label.GetLabelEditForm(ctx)
38+
if ctx.Written() {
4339
return
4440
}
4541

@@ -55,20 +51,22 @@ func NewLabel(ctx *context.Context) {
5551
ctx.ServerError("NewLabel", err)
5652
return
5753
}
58-
ctx.Redirect(ctx.Org.OrgLink + "/settings/labels")
54+
ctx.JSONRedirect(ctx.Org.OrgLink + "/settings/labels")
5955
}
6056

6157
// UpdateLabel update a label's name and color
6258
func UpdateLabel(ctx *context.Context) {
63-
form := web.GetForm(ctx).(*forms.CreateLabelForm)
59+
form := shared_label.GetLabelEditForm(ctx)
60+
if ctx.Written() {
61+
return
62+
}
63+
6464
l, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, form.ID)
65-
if err != nil {
66-
switch {
67-
case issues_model.IsErrOrgLabelNotExist(err):
68-
ctx.HTTPError(http.StatusNotFound)
69-
default:
70-
ctx.ServerError("UpdateLabel", err)
71-
}
65+
if errors.Is(err, util.ErrNotExist) {
66+
ctx.JSONErrorNotFound()
67+
return
68+
} else if err != nil {
69+
ctx.ServerError("GetLabelInOrgByID", err)
7270
return
7371
}
7472

@@ -82,7 +80,7 @@ func UpdateLabel(ctx *context.Context) {
8280
ctx.ServerError("UpdateLabel", err)
8381
return
8482
}
85-
ctx.Redirect(ctx.Org.OrgLink + "/settings/labels")
83+
ctx.JSONRedirect(ctx.Org.OrgLink + "/settings/labels")
8684
}
8785

8886
// DeleteLabel delete a label

routers/web/repo/issue_label.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package repo
55

66
import (
7+
"errors"
78
"net/http"
89

910
"code.gitea.io/gitea/models/db"
@@ -13,7 +14,9 @@ import (
1314
"code.gitea.io/gitea/modules/log"
1415
repo_module "code.gitea.io/gitea/modules/repository"
1516
"code.gitea.io/gitea/modules/templates"
17+
"code.gitea.io/gitea/modules/util"
1618
"code.gitea.io/gitea/modules/web"
19+
shared_label "code.gitea.io/gitea/routers/web/shared/label"
1720
"code.gitea.io/gitea/services/context"
1821
"code.gitea.io/gitea/services/forms"
1922
issue_service "code.gitea.io/gitea/services/issue"
@@ -100,13 +103,8 @@ func RetrieveLabelsForList(ctx *context.Context) {
100103

101104
// NewLabel create new label for repository
102105
func NewLabel(ctx *context.Context) {
103-
form := web.GetForm(ctx).(*forms.CreateLabelForm)
104-
ctx.Data["Title"] = ctx.Tr("repo.labels")
105-
ctx.Data["PageIsLabels"] = true
106-
107-
if ctx.HasError() {
108-
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
109-
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
106+
form := shared_label.GetLabelEditForm(ctx)
107+
if ctx.Written() {
110108
return
111109
}
112110

@@ -122,34 +120,36 @@ func NewLabel(ctx *context.Context) {
122120
ctx.ServerError("NewLabel", err)
123121
return
124122
}
125-
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
123+
ctx.JSONRedirect(ctx.Repo.RepoLink + "/labels")
126124
}
127125

128126
// UpdateLabel update a label's name and color
129127
func UpdateLabel(ctx *context.Context) {
130-
form := web.GetForm(ctx).(*forms.CreateLabelForm)
128+
form := shared_label.GetLabelEditForm(ctx)
129+
if ctx.Written() {
130+
return
131+
}
132+
131133
l, err := issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, form.ID)
132-
if err != nil {
133-
switch {
134-
case issues_model.IsErrRepoLabelNotExist(err):
135-
ctx.HTTPError(http.StatusNotFound)
136-
default:
137-
ctx.ServerError("UpdateLabel", err)
138-
}
134+
if errors.Is(err, util.ErrNotExist) {
135+
ctx.JSONErrorNotFound()
136+
return
137+
} else if err != nil {
138+
ctx.ServerError("GetLabelInRepoByID", err)
139139
return
140140
}
141+
141142
l.Name = form.Title
142143
l.Exclusive = form.Exclusive
143144
l.ExclusiveOrder = form.ExclusiveOrder
144145
l.Description = form.Description
145146
l.Color = form.Color
146-
147147
l.SetArchived(form.IsArchived)
148148
if err := issues_model.UpdateLabel(ctx, l); err != nil {
149149
ctx.ServerError("UpdateLabel", err)
150150
return
151151
}
152-
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
152+
ctx.JSONRedirect(ctx.Repo.RepoLink + "/labels")
153153
}
154154

155155
// DeleteLabel delete a label

0 commit comments

Comments
 (0)