2017-04-19 06:02:20 +03:00
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"fmt"
"regexp"
"strings"
2021-01-26 18:36:53 +03:00
"gitea.com/go-chi/binding"
2019-09-09 08:48:21 +03:00
"github.com/gobwas/glob"
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 17:28:55 +03:00
// ErrRegexPattern is returned when a regex pattern is invalid
ErrRegexPattern = "RegexPattern"
2017-04-19 06:02:20 +03:00
)
var (
2020-04-08 05:54:46 +03:00
// GitRefNamePatternInvalid is regular expression with unallowed characters in git reference name
2019-03-26 22:59:48 +03:00
// They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere.
// They cannot have question-mark ?, asterisk *, or open bracket [ anywhere
2020-04-08 05:54:46 +03:00
GitRefNamePatternInvalid = regexp . MustCompile ( ` [\000-\037\177 \\~^:?*[]+ ` )
2017-04-19 06:02:20 +03:00
)
2020-04-08 05:54:46 +03:00
// CheckGitRefAdditionalRulesValid check name is valid on additional rules
func CheckGitRefAdditionalRulesValid ( name string ) bool {
// Additional rules as described at https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
if strings . HasPrefix ( name , "/" ) || strings . HasSuffix ( name , "/" ) ||
strings . HasSuffix ( name , "." ) || strings . Contains ( name , ".." ) ||
strings . Contains ( name , "//" ) || strings . Contains ( name , "@{" ) ||
name == "@" {
return false
}
parts := strings . Split ( name , "/" )
for _ , part := range parts {
if strings . HasSuffix ( part , ".lock" ) || strings . HasPrefix ( part , "." ) {
return false
}
}
return true
}
2017-04-19 06:02:20 +03:00
// AddBindingRules adds additional binding rules
func AddBindingRules ( ) {
addGitRefNameBindingRule ( )
addValidURLBindingRule ( )
2019-09-09 08:48:21 +03:00
addGlobPatternRule ( )
2021-06-25 17:28:55 +03:00
addRegexPatternRule ( )
addGlobOrRegexPatternRule ( )
2017-04-19 06:02:20 +03:00
}
func addGitRefNameBindingRule ( ) {
// Git refname validation rule
binding . AddRule ( & binding . Rule {
IsMatch : func ( rule string ) bool {
return strings . HasPrefix ( rule , "GitRefName" )
} ,
IsValid : func ( errs binding . Errors , name string , val interface { } ) ( bool , binding . Errors ) {
str := fmt . Sprintf ( "%v" , val )
2020-04-08 05:54:46 +03:00
if GitRefNamePatternInvalid . MatchString ( str ) {
2017-04-19 06:02:20 +03:00
errs . Add ( [ ] string { name } , ErrGitRefName , "GitRefName" )
return false , errs
}
2020-04-08 05:54:46 +03:00
if ! CheckGitRefAdditionalRulesValid ( str ) {
2017-04-19 06:02:20 +03:00
errs . Add ( [ ] string { name } , ErrGitRefName , "GitRefName" )
return false , errs
}
return true , errs
} ,
} )
}
func addValidURLBindingRule ( ) {
// URL validation rule
binding . AddRule ( & binding . Rule {
IsMatch : func ( rule string ) bool {
return strings . HasPrefix ( rule , "ValidUrl" )
} ,
IsValid : func ( errs binding . Errors , name string , val interface { } ) ( bool , binding . Errors ) {
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
} ,
} )
}
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 17:28:55 +03:00
IsValid : globPatternValidator ,
} )
}
func globPatternValidator ( errs binding . Errors , name string , val interface { } ) ( 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 interface { } ) ( 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"
} ,
2019-09-09 08:48:21 +03:00
IsValid : func ( errs binding . Errors , name string , val interface { } ) ( bool , binding . Errors ) {
2021-06-25 17:28:55 +03:00
str := strings . TrimSpace ( fmt . Sprintf ( "%v" , val ) )
2019-09-09 08:48:21 +03:00
2021-06-25 17:28:55 +03: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 17:28:55 +03:00
return globPatternValidator ( errs , name , val )
2019-09-09 08:48:21 +03:00
} ,
} )
}
2017-04-19 06:02:20 +03:00
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
}