// Copyright 2017 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package validation import ( "fmt" "regexp" "strings" "forgejo.org/modules/auth" "forgejo.org/modules/git" "forgejo.org/modules/util" "code.forgejo.org/go-chi/binding" "github.com/gobwas/glob") const ( // ErrGitRefName is git reference name error ErrGitRefName = "GitRefNameError" // ErrGlobPattern is returned when glob pattern is invalid ErrGlobPattern = "GlobPattern" // ErrRegexPattern is returned when a regex pattern is invalid ErrRegexPattern = "RegexPattern" // ErrUsername is username error ErrUsername = "UsernameError" // ErrInvalidGroupTeamMap is returned when a group team mapping is invalid ErrInvalidGroupTeamMap = "InvalidGroupTeamMap" // ErrEmail is returned when an email address is invalid ErrEmail = "Email") // AddBindingRules adds additional binding rulesfunc AddBindingRules() { addGitRefNameBindingRule() addValidURLListBindingRule() addValidURLBindingRule() addValidSiteURLBindingRule() addGlobPatternRule() addRegexPatternRule() addGlobOrRegexPatternRule() addUsernamePatternRule() addValidGroupTeamMapRule() addEmailBindingRules()} func addGitRefNameBindingRule() { // Git refname validation rule binding.AddRule(&binding.Rule{ IsMatch: func(rule string) bool { return rule == "GitRefName" }, IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := fmt.Sprintf("%v", val) if !git.IsValidRefPattern(str) { errs.Add([]string{name}, ErrGitRefName, "GitRefName") return false, errs } return true, errs }, })} 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 }, })} func addValidURLBindingRule() { // URL validation rule binding.AddRule(&binding.Rule{ IsMatch: func(rule string) bool { return rule == "ValidUrl" }, IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := fmt.Sprintf("%v", val) if len(str) != 0 && !IsValidURL(str) { errs.Add([]string{name}, binding.ERR_URL, "Url") return false, errs } return true, errs }, })} func addValidSiteURLBindingRule() { // URL validation rule binding.AddRule(&binding.Rule{ IsMatch: func(rule string) bool { return rule == "ValidSiteUrl" }, IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { 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 }, })} func addGlobPatternRule() { binding.AddRule(&binding.Rule{ IsMatch: func(rule string) bool { return rule == "GlobPattern" }, IsValid: globPatternValidator, })} func globPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) { 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, })} func regexPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) { 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" }, IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := strings.TrimSpace(fmt.Sprintf("%v", val)) if len(str) >= 2 && strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") { return regexPatternValidator(errs, name, str[1:len(str)-1]) } return globPatternValidator(errs, name, val) }, })} func addUsernamePatternRule() { binding.AddRule(&binding.Rule{ IsMatch: func(rule string) bool { return rule == "Username" }, IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := fmt.Sprintf("%v", val) if !IsValidUsername(str) { errs.Add([]string{name}, ErrUsername, "invalid username") return false, errs } return true, errs }, })} func addValidGroupTeamMapRule() { binding.AddRule(&binding.Rule{ IsMatch: func(rule string) bool { return rule == "ValidGroupTeamMap" }, IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { _, err := auth.UnmarshalGroupTeamMapping(fmt.Sprintf("%v", val)) if err != nil { errs.Add([]string{name}, ErrInvalidGroupTeamMap, err.Error()) return false, errs } return true, errs }, })} func addEmailBindingRules() { binding.AddRule(&binding.Rule{ IsMatch: func(rule string) bool { return strings.HasPrefix(rule, "EmailWithAllowedDomain") }, IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { if err := ValidateEmail(fmt.Sprintf("%v", val)); err != nil { errs.Add([]string{name}, ErrEmail, err.Error()) return false, errs } return true, errs }, }) binding.AddRule(&binding.Rule{ IsMatch: func(rule string) bool { return strings.HasPrefix(rule, "EmailForAdmin") }, IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { if err := ValidateEmailForAdmin(fmt.Sprintf("%v", val)); err != nil { errs.Add([]string{name}, ErrEmail, err.Error()) return false, errs } return true, errs }, })} func portOnly(hostport string) string { colon := strings.IndexByte(hostport, ':') if colon == -1 { return "" } if i := strings.Index(hostport, "]:"); i != -1 { return hostport[i+len("]:"):] } if strings.Contains(hostport, "]") { return "" } return hostport[colon+len(":"):]} func validPort(p string) bool { for _, r := range []byte(p) { if r < '0' || r > '9' { return false } } return true}
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package validation
import (
"fmt"
"regexp"
"strings"
"forgejo.org/modules/auth"
"forgejo.org/modules/git"
"forgejo.org/modules/util"
"code.forgejo.org/go-chi/binding"
"github.com/gobwas/glob"
)
const (
// ErrGitRefName is git reference name error
ErrGitRefName = "GitRefNameError"
// ErrGlobPattern is returned when glob pattern is invalid
ErrGlobPattern = "GlobPattern"
// ErrRegexPattern is returned when a regex pattern is invalid
ErrRegexPattern = "RegexPattern"
// ErrUsername is username error
ErrUsername = "UsernameError"
// ErrInvalidGroupTeamMap is returned when a group team mapping is invalid
ErrInvalidGroupTeamMap = "InvalidGroupTeamMap"
// ErrEmail is returned when an email address is invalid
ErrEmail = "Email"
)
// AddBindingRules adds additional binding rules
func AddBindingRules() {
addGitRefNameBindingRule()
addValidURLListBindingRule()
addValidURLBindingRule()
addValidSiteURLBindingRule()
addGlobPatternRule()
addRegexPatternRule()
addGlobOrRegexPatternRule()
addUsernamePatternRule()
addValidGroupTeamMapRule()
addEmailBindingRules()
}
func addGitRefNameBindingRule() {
// Git refname validation rule
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "GitRefName"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if !git.IsValidRefPattern(str) {
errs.Add([]string{name}, ErrGitRefName, "GitRefName")
return false, errs
}
return true, errs
},
})
}
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
},
})
}
func addValidURLBindingRule() {
// URL validation rule
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "ValidUrl"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if len(str) != 0 && !IsValidURL(str) {
errs.Add([]string{name}, binding.ERR_URL, "Url")
return false, errs
}
return true, errs
},
})
}
func addValidSiteURLBindingRule() {
// URL validation rule
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "ValidSiteUrl"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
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
},
})
}
func addGlobPatternRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "GlobPattern"
},
IsValid: globPatternValidator,
})
}
func globPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) {
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,
})
}
func regexPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) {
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"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := strings.TrimSpace(fmt.Sprintf("%v", val))
if len(str) >= 2 && strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") {
return regexPatternValidator(errs, name, str[1:len(str)-1])
}
return globPatternValidator(errs, name, val)
},
})
}
func addUsernamePatternRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "Username"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if !IsValidUsername(str) {
errs.Add([]string{name}, ErrUsername, "invalid username")
return false, errs
}
return true, errs
},
})
}
func addValidGroupTeamMapRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "ValidGroupTeamMap"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
_, err := auth.UnmarshalGroupTeamMapping(fmt.Sprintf("%v", val))
if err != nil {
errs.Add([]string{name}, ErrInvalidGroupTeamMap, err.Error())
return false, errs
}
return true, errs
},
})
}
func addEmailBindingRules() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return strings.HasPrefix(rule, "EmailWithAllowedDomain")
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
if err := ValidateEmail(fmt.Sprintf("%v", val)); err != nil {
errs.Add([]string{name}, ErrEmail, err.Error())
return false, errs
}
return true, errs
},
})
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return strings.HasPrefix(rule, "EmailForAdmin")
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
if err := ValidateEmailForAdmin(fmt.Sprintf("%v", val)); err != nil {
errs.Add([]string{name}, ErrEmail, err.Error())
return false, errs
}
return true, errs
},
})
}
func portOnly(hostport string) string {
colon := strings.IndexByte(hostport, ':')
if colon == -1 {
return ""
}
if i := strings.Index(hostport, "]:"); i != -1 {
return hostport[i+len("]:"):]
}
if strings.Contains(hostport, "]") {
return ""
}
return hostport[colon+len(":"):]
}
func validPort(p string) bool {
for _, r := range []byte(p) {
if r < '0' || r > '9' {
return false
}
}
return true
}