2017-04-19 06:02:20 +03:00
|
|
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
2022-11-27 13:20:29 -05:00
|
|
|
// SPDX-License-Identifier: MIT
|
2017-04-19 06:02:20 +03:00
|
|
|
|
|
|
|
|
package validation
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2026-04-15 01:24:39 +08:00
|
|
|
"io"
|
2017-04-19 06:02:20 +03:00
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
|
2023-02-08 07:44:42 +01:00
|
|
|
"code.gitea.io/gitea/modules/auth"
|
2022-09-04 11:47:56 +01:00
|
|
|
"code.gitea.io/gitea/modules/git"
|
2025-09-14 02:01:00 +08:00
|
|
|
"code.gitea.io/gitea/modules/glob"
|
2026-04-15 01:24:39 +08:00
|
|
|
"code.gitea.io/gitea/modules/json"
|
2024-11-27 20:50:27 -06:00
|
|
|
"code.gitea.io/gitea/modules/util"
|
2022-09-04 11:47:56 +01:00
|
|
|
|
2021-01-26 23:36:53 +08:00
|
|
|
"gitea.com/go-chi/binding"
|
2017-04-19 06:02:20 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// ErrGitRefName is git reference name error
|
|
|
|
|
ErrGitRefName = "GitRefNameError"
|
2019-09-09 08:48:21 +03:00
|
|
|
// ErrGlobPattern is returned when glob pattern is invalid
|
|
|
|
|
ErrGlobPattern = "GlobPattern"
|
2021-06-25 16:28:55 +02:00
|
|
|
// ErrRegexPattern is returned when a regex pattern is invalid
|
|
|
|
|
ErrRegexPattern = "RegexPattern"
|
2022-11-04 17:04:08 +08:00
|
|
|
// ErrUsername is username error
|
|
|
|
|
ErrUsername = "UsernameError"
|
2023-02-08 07:44:42 +01:00
|
|
|
// ErrInvalidGroupTeamMap is returned when a group team mapping is invalid
|
|
|
|
|
ErrInvalidGroupTeamMap = "InvalidGroupTeamMap"
|
2026-03-22 16:49:45 +01:00
|
|
|
// ErrInvalidBadgeSlug is returned when a badge slug is invalid
|
|
|
|
|
ErrInvalidBadgeSlug = "InvalidBadgeSlug"
|
2017-04-19 06:02:20 +03:00
|
|
|
)
|
|
|
|
|
|
2026-04-15 01:24:39 +08:00
|
|
|
type jsonProvider struct{}
|
|
|
|
|
|
|
|
|
|
func (j jsonProvider) Marshal(v any) ([]byte, error) { return json.Marshal(v) }
|
|
|
|
|
|
|
|
|
|
func (j jsonProvider) Unmarshal(data []byte, v any) error { return json.Unmarshal(data, v) }
|
|
|
|
|
|
|
|
|
|
func (j jsonProvider) NewDecoder(reader io.Reader) binding.JSONDecoder {
|
|
|
|
|
return json.NewDecoder(reader)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j jsonProvider) NewEncoder(writer io.Writer) binding.JSONEncoder {
|
|
|
|
|
return json.NewEncoder(writer)
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-19 06:02:20 +03:00
|
|
|
// AddBindingRules adds additional binding rules
|
|
|
|
|
func AddBindingRules() {
|
2026-04-15 01:24:39 +08:00
|
|
|
binding.JSONProvider = jsonProvider{}
|
2017-04-19 06:02:20 +03:00
|
|
|
addGitRefNameBindingRule()
|
2024-11-27 20:50:27 -06:00
|
|
|
addValidURLListBindingRule()
|
2017-04-19 06:02:20 +03:00
|
|
|
addValidURLBindingRule()
|
2021-06-26 00:38:27 +02:00
|
|
|
addValidSiteURLBindingRule()
|
2019-09-09 08:48:21 +03:00
|
|
|
addGlobPatternRule()
|
2021-06-25 16:28:55 +02:00
|
|
|
addRegexPatternRule()
|
|
|
|
|
addGlobOrRegexPatternRule()
|
2022-11-04 17:04:08 +08:00
|
|
|
addUsernamePatternRule()
|
2023-02-08 07:44:42 +01:00
|
|
|
addValidGroupTeamMapRule()
|
2026-03-22 16:49:45 +01:00
|
|
|
addSlugPatternRule()
|
2017-04-19 06:02:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func addGitRefNameBindingRule() {
|
|
|
|
|
// Git refname validation rule
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
2024-11-27 20:50:27 -06:00
|
|
|
return rule == "GitRefName"
|
2017-04-19 06:02:20 +03:00
|
|
|
},
|
2023-07-04 20:36:08 +02:00
|
|
|
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
2017-04-19 06:02:20 +03:00
|
|
|
str := fmt.Sprintf("%v", val)
|
|
|
|
|
|
2022-09-04 11:47:56 +01:00
|
|
|
if !git.IsValidRefPattern(str) {
|
2017-04-19 06:02:20 +03:00
|
|
|
errs.Add([]string{name}, ErrGitRefName, "GitRefName")
|
|
|
|
|
return false, errs
|
|
|
|
|
}
|
|
|
|
|
return true, errs
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-27 20:50:27 -06:00
|
|
|
func addValidURLListBindingRule() {
|
|
|
|
|
// URL validation rule
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
|
|
|
|
return rule == "ValidUrlList"
|
|
|
|
|
},
|
|
|
|
|
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
|
|
|
|
str := fmt.Sprintf("%v", val)
|
|
|
|
|
if len(str) == 0 {
|
|
|
|
|
errs.Add([]string{name}, binding.ERR_URL, "Url")
|
|
|
|
|
return false, errs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ok := true
|
|
|
|
|
urls := util.SplitTrimSpace(str, "\n")
|
|
|
|
|
for _, u := range urls {
|
|
|
|
|
if !IsValidURL(u) {
|
|
|
|
|
ok = false
|
|
|
|
|
errs.Add([]string{name}, binding.ERR_URL, u)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ok, errs
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-19 06:02:20 +03:00
|
|
|
func addValidURLBindingRule() {
|
|
|
|
|
// URL validation rule
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
2024-11-27 20:50:27 -06:00
|
|
|
return rule == "ValidUrl"
|
2017-04-19 06:02:20 +03:00
|
|
|
},
|
2023-07-04 20:36:08 +02:00
|
|
|
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
2017-04-19 06:02:20 +03:00
|
|
|
str := fmt.Sprintf("%v", val)
|
2018-08-15 09:29:37 +03:00
|
|
|
if len(str) != 0 && !IsValidURL(str) {
|
|
|
|
|
errs.Add([]string{name}, binding.ERR_URL, "Url")
|
|
|
|
|
return false, errs
|
2017-04-19 06:02:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, errs
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-26 00:38:27 +02:00
|
|
|
func addValidSiteURLBindingRule() {
|
|
|
|
|
// URL validation rule
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
2024-11-27 20:50:27 -06:00
|
|
|
return rule == "ValidSiteUrl"
|
2021-06-26 00:38:27 +02:00
|
|
|
},
|
2023-07-04 20:36:08 +02:00
|
|
|
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
2021-06-26 00:38:27 +02:00
|
|
|
str := fmt.Sprintf("%v", val)
|
|
|
|
|
if len(str) != 0 && !IsValidSiteURL(str) {
|
|
|
|
|
errs.Add([]string{name}, binding.ERR_URL, "Url")
|
|
|
|
|
return false, errs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, errs
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 16:49:45 +01:00
|
|
|
func addSlugPatternRule() {
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
|
|
|
|
return rule == "BadgeSlug"
|
|
|
|
|
},
|
|
|
|
|
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
|
|
|
|
str := fmt.Sprintf("%v", val)
|
|
|
|
|
if !IsValidBadgeSlug(str) {
|
|
|
|
|
errs.Add([]string{name}, ErrInvalidBadgeSlug, "invalid badge slug")
|
|
|
|
|
return false, errs
|
|
|
|
|
}
|
|
|
|
|
return true, errs
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 08:48:21 +03:00
|
|
|
func addGlobPatternRule() {
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
|
|
|
|
return rule == "GlobPattern"
|
|
|
|
|
},
|
2021-06-25 16:28:55 +02:00
|
|
|
IsValid: globPatternValidator,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-04 20:36:08 +02:00
|
|
|
func globPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
2021-06-25 16:28:55 +02:00
|
|
|
str := fmt.Sprintf("%v", val)
|
|
|
|
|
|
|
|
|
|
if len(str) != 0 {
|
|
|
|
|
if _, err := glob.Compile(str); err != nil {
|
|
|
|
|
errs.Add([]string{name}, ErrGlobPattern, err.Error())
|
|
|
|
|
return false, errs
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, errs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func addRegexPatternRule() {
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
|
|
|
|
return rule == "RegexPattern"
|
|
|
|
|
},
|
|
|
|
|
IsValid: regexPatternValidator,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-04 20:36:08 +02:00
|
|
|
func regexPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
2021-06-25 16:28:55 +02:00
|
|
|
str := fmt.Sprintf("%v", val)
|
|
|
|
|
|
|
|
|
|
if _, err := regexp.Compile(str); err != nil {
|
|
|
|
|
errs.Add([]string{name}, ErrRegexPattern, err.Error())
|
|
|
|
|
return false, errs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, errs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func addGlobOrRegexPatternRule() {
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
|
|
|
|
return rule == "GlobOrRegexPattern"
|
|
|
|
|
},
|
2023-07-04 20:36:08 +02:00
|
|
|
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
2021-06-25 16:28:55 +02:00
|
|
|
str := strings.TrimSpace(fmt.Sprintf("%v", val))
|
2019-09-09 08:48:21 +03:00
|
|
|
|
2021-06-25 16:28:55 +02:00
|
|
|
if len(str) >= 2 && strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") {
|
|
|
|
|
return regexPatternValidator(errs, name, str[1:len(str)-1])
|
2019-09-09 08:48:21 +03:00
|
|
|
}
|
2021-06-25 16:28:55 +02:00
|
|
|
return globPatternValidator(errs, name, val)
|
2019-09-09 08:48:21 +03:00
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-04 17:04:08 +08:00
|
|
|
func addUsernamePatternRule() {
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
|
|
|
|
return rule == "Username"
|
|
|
|
|
},
|
2023-07-04 20:36:08 +02:00
|
|
|
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
2022-11-04 17:04:08 +08:00
|
|
|
str := fmt.Sprintf("%v", val)
|
|
|
|
|
if !IsValidUsername(str) {
|
|
|
|
|
errs.Add([]string{name}, ErrUsername, "invalid username")
|
|
|
|
|
return false, errs
|
|
|
|
|
}
|
|
|
|
|
return true, errs
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-08 07:44:42 +01:00
|
|
|
func addValidGroupTeamMapRule() {
|
|
|
|
|
binding.AddRule(&binding.Rule{
|
|
|
|
|
IsMatch: func(rule string) bool {
|
2024-11-27 20:50:27 -06:00
|
|
|
return rule == "ValidGroupTeamMap"
|
2023-02-08 07:44:42 +01:00
|
|
|
},
|
2023-07-04 20:36:08 +02:00
|
|
|
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
2023-02-08 07:44:42 +01:00
|
|
|
_, err := auth.UnmarshalGroupTeamMapping(fmt.Sprintf("%v", val))
|
|
|
|
|
if err != nil {
|
|
|
|
|
errs.Add([]string{name}, ErrInvalidGroupTeamMap, err.Error())
|
|
|
|
|
return false, errs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, errs
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-19 06:02:20 +03:00
|
|
|
func portOnly(hostport string) string {
|
2025-12-04 10:06:44 +01:00
|
|
|
_, after, ok := strings.Cut(hostport, ":")
|
|
|
|
|
if !ok {
|
2017-04-19 06:02:20 +03:00
|
|
|
return ""
|
|
|
|
|
}
|
2026-01-24 09:58:28 +01:00
|
|
|
if _, after2, ok2 := strings.Cut(hostport, "]:"); ok2 {
|
|
|
|
|
return after2
|
2017-04-19 06:02:20 +03:00
|
|
|
}
|
|
|
|
|
if strings.Contains(hostport, "]") {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
2025-12-04 10:06:44 +01:00
|
|
|
return after
|
2017-04-19 06:02:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func validPort(p string) bool {
|
|
|
|
|
for _, r := range []byte(p) {
|
|
|
|
|
if r < '0' || r > '9' {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|