Skip to content

Commit 7fa60c2

Browse files
committed
fix
1 parent 3e1b63f commit 7fa60c2

30 files changed

+667
-606
lines changed

models/db/name.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
"code.gitea.io/gitea/modules/util"
1212
)
1313

14-
var ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
15-
1614
// ErrNameReserved represents a "reserved name" error.
1715
type ErrNameReserved struct {
1816
Name string
@@ -79,7 +77,7 @@ func (err ErrNameCharsNotAllowed) Unwrap() error {
7977
func IsUsableName(reservedNames, reservedPatterns []string, name string) error {
8078
name = strings.TrimSpace(strings.ToLower(name))
8179
if utf8.RuneCountInString(name) == 0 {
82-
return ErrNameEmpty
80+
return util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
8381
}
8482

8583
for i := range reservedNames {

models/repo/repo.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"regexp"
1515
"strconv"
1616
"strings"
17+
"sync"
1718

1819
"code.gitea.io/gitea/models/db"
1920
"code.gitea.io/gitea/models/unit"
@@ -61,20 +62,30 @@ func (err ErrRepoIsArchived) Error() string {
6162
return fmt.Sprintf("%s is archived", err.Repo.LogString())
6263
}
6364

64-
var (
65-
validRepoNamePattern = regexp.MustCompile(`[-.\w]+`)
66-
invalidRepoNamePattern = regexp.MustCompile(`[.]{2,}`)
67-
reservedRepoNames = []string{".", "..", "-"}
68-
reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
69-
)
65+
type globalVarsStruct struct {
66+
validRepoNamePattern *regexp.Regexp
67+
invalidRepoNamePattern *regexp.Regexp
68+
reservedRepoNames []string
69+
reservedRepoPatterns []string
70+
}
71+
72+
var globalVars = sync.OnceValue(func() *globalVarsStruct {
73+
return &globalVarsStruct{
74+
validRepoNamePattern: regexp.MustCompile(`[-.\w]+`),
75+
invalidRepoNamePattern: regexp.MustCompile(`[.]{2,}`),
76+
reservedRepoNames: []string{".", "..", "-"},
77+
reservedRepoPatterns: []string{"*.git", "*.wiki", "*.rss", "*.atom"},
78+
}
79+
})
7080

7181
// IsUsableRepoName returns true when name is usable
7282
func IsUsableRepoName(name string) error {
73-
if !validRepoNamePattern.MatchString(name) || invalidRepoNamePattern.MatchString(name) {
83+
vars := globalVars()
84+
if !vars.validRepoNamePattern.MatchString(name) || vars.invalidRepoNamePattern.MatchString(name) {
7485
// Note: usually this error is normally caught up earlier in the UI
7586
return db.ErrNameCharsNotAllowed{Name: name}
7687
}
77-
return db.IsUsableName(reservedRepoNames, reservedRepoPatterns, name)
88+
return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoPatterns, name)
7889
}
7990

8091
// TrustModelType defines the types of trust model for this repository

models/repo/repo_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,5 @@ func TestIsUsableRepoName(t *testing.T) {
219219
assert.Error(t, IsUsableRepoName("the..repo"))
220220
assert.Error(t, IsUsableRepoName("foo.wiki"))
221221
assert.Error(t, IsUsableRepoName("foo.git"))
222+
assert.Error(t, IsUsableRepoName("foo.RSS"))
222223
}

models/user/user.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -502,10 +502,10 @@ func (u *User) IsMailable() bool {
502502
return u.IsActive
503503
}
504504

505-
// IsUserExist checks if given user name exist,
506-
// the user name should be noncased unique.
505+
// IsUserExist checks if given username exist,
506+
// the username should be non-cased unique.
507507
// If uid is presented, then check will rule out that one,
508-
// it is used when update a user name in settings page.
508+
// it is used when update a username in settings page.
509509
func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
510510
if len(name) == 0 {
511511
return false, nil
@@ -515,7 +515,7 @@ func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
515515
Get(&User{LowerName: strings.ToLower(name)})
516516
}
517517

518-
// Note: As of the beginning of 2022, it is recommended to use at least
518+
// SaltByteLength as of the beginning of 2022, it is recommended to use at least
519519
// 64 bits of salt, but NIST is already recommending to use to 128 bits.
520520
// (16 bytes = 16 * 8 = 128 bits)
521521
const SaltByteLength = 16

models/user/user_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,21 @@ import (
2525
"github.com/stretchr/testify/assert"
2626
)
2727

28+
func TestIsUsableUsername(t *testing.T) {
29+
assert.NoError(t, user_model.IsUsableUsername("a"))
30+
assert.NoError(t, user_model.IsUsableUsername("foo.wiki"))
31+
assert.NoError(t, user_model.IsUsableUsername("foo.git"))
32+
33+
assert.Error(t, user_model.IsUsableUsername("a--b"))
34+
assert.Error(t, user_model.IsUsableUsername("-1_."))
35+
assert.Error(t, user_model.IsUsableUsername(".profile"))
36+
assert.Error(t, user_model.IsUsableUsername("-"))
37+
assert.Error(t, user_model.IsUsableUsername("🌞"))
38+
assert.Error(t, user_model.IsUsableUsername("the..repo"))
39+
assert.Error(t, user_model.IsUsableUsername("foo.RSS"))
40+
assert.Error(t, user_model.IsUsableUsername("foo.PnG"))
41+
}
42+
2843
func TestOAuth2Application_LoadUser(t *testing.T) {
2944
assert.NoError(t, unittest.PrepareTestDatabase())
3045
app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1})

modules/validation/glob_pattern_test.go

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,39 @@ func getGlobPatternErrorString(pattern string) string {
1919
return ""
2020
}
2121

22-
var globValidationTestCases = []validationTestCase{
23-
{
24-
description: "Empty glob pattern",
25-
data: TestForm{
26-
GlobPattern: "",
27-
},
28-
expectedErrors: binding.Errors{},
29-
},
30-
{
31-
description: "Valid glob",
32-
data: TestForm{
33-
GlobPattern: "{master,release*}",
34-
},
35-
expectedErrors: binding.Errors{},
36-
},
22+
func Test_GlobPatternValidation(t *testing.T) {
23+
AddBindingRules()
3724

38-
{
39-
description: "Invalid glob",
40-
data: TestForm{
41-
GlobPattern: "[a-",
25+
globValidationTestCases := []validationTestCase{
26+
{
27+
description: "Empty glob pattern",
28+
data: TestForm{
29+
GlobPattern: "",
30+
},
31+
expectedErrors: binding.Errors{},
4232
},
43-
expectedErrors: binding.Errors{
44-
binding.Error{
45-
FieldNames: []string{"GlobPattern"},
46-
Classification: ErrGlobPattern,
47-
Message: getGlobPatternErrorString("[a-"),
33+
{
34+
description: "Valid glob",
35+
data: TestForm{
36+
GlobPattern: "{master,release*}",
4837
},
38+
expectedErrors: binding.Errors{},
4939
},
50-
},
51-
}
5240

53-
func Test_GlobPatternValidation(t *testing.T) {
54-
AddBindingRules()
41+
{
42+
description: "Invalid glob",
43+
data: TestForm{
44+
GlobPattern: "[a-",
45+
},
46+
expectedErrors: binding.Errors{
47+
binding.Error{
48+
FieldNames: []string{"GlobPattern"},
49+
Classification: ErrGlobPattern,
50+
Message: getGlobPatternErrorString("[a-"),
51+
},
52+
},
53+
},
54+
}
5555

5656
for _, testCase := range globValidationTestCases {
5757
t.Run(testCase.description, func(t *testing.T) {

modules/validation/helpers.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,26 @@ import (
88
"net/url"
99
"regexp"
1010
"strings"
11+
"sync"
1112

1213
"code.gitea.io/gitea/modules/setting"
1314

1415
"github.com/gobwas/glob"
1516
)
1617

17-
var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`)
18+
type globalVarsStruct struct {
19+
externalTrackerRegex *regexp.Regexp
20+
validUsernamePattern *regexp.Regexp
21+
invalidUsernamePattern *regexp.Regexp
22+
}
23+
24+
var globalVars = sync.OnceValue(func() *globalVarsStruct {
25+
return &globalVarsStruct{
26+
externalTrackerRegex: regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`),
27+
validUsernamePattern: regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`),
28+
invalidUsernamePattern: regexp.MustCompile(`[-._]{2,}|[-._]$`), // No consecutive or trailing non-alphanumeric chars
29+
}
30+
})
1831

1932
func isLoopbackIP(ip string) bool {
2033
return net.ParseIP(ip).IsLoopback()
@@ -105,9 +118,9 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
105118
if !IsValidExternalURL(uri) {
106119
return false
107120
}
108-
121+
vars := globalVars()
109122
// check for typoed variables like /{index/ or /[repo}
110-
for _, match := range externalTrackerRegex.FindAllStringSubmatch(uri, -1) {
123+
for _, match := range vars.externalTrackerRegex.FindAllStringSubmatch(uri, -1) {
111124
if (match[1] == "{" || match[2] == "}") && (match[1] != "{" || match[2] != "}") {
112125
return false
113126
}
@@ -116,14 +129,10 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
116129
return true
117130
}
118131

119-
var (
120-
validUsernamePattern = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`)
121-
invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) // No consecutive or trailing non-alphanumeric chars
122-
)
123-
124132
// IsValidUsername checks if username is valid
125133
func IsValidUsername(name string) bool {
126134
// It is difficult to find a single pattern that is both readable and effective,
127135
// but it's easier to use positive and negative checks.
128-
return validUsernamePattern.MatchString(name) && !invalidUsernamePattern.MatchString(name)
136+
vars := globalVars()
137+
return vars.validUsernamePattern.MatchString(name) && !vars.invalidUsernamePattern.MatchString(name)
129138
}

0 commit comments

Comments
 (0)