code.gitea.io/gitea@v1.22.3/modules/validation/helpers.go (about) 1 // Copyright 2018 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package validation 5 6 import ( 7 "net" 8 "net/url" 9 "regexp" 10 "strings" 11 12 "code.gitea.io/gitea/modules/setting" 13 14 "github.com/gobwas/glob" 15 ) 16 17 var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`) 18 19 func isLoopbackIP(ip string) bool { 20 return net.ParseIP(ip).IsLoopback() 21 } 22 23 // IsValidURL checks if URL is valid 24 func IsValidURL(uri string) bool { 25 if u, err := url.ParseRequestURI(uri); err != nil || 26 (u.Scheme != "http" && u.Scheme != "https") || 27 !validPort(portOnly(u.Host)) { 28 return false 29 } 30 31 return true 32 } 33 34 // IsValidSiteURL checks if URL is valid 35 func IsValidSiteURL(uri string) bool { 36 u, err := url.ParseRequestURI(uri) 37 if err != nil { 38 return false 39 } 40 41 if !validPort(portOnly(u.Host)) { 42 return false 43 } 44 45 for _, scheme := range setting.Service.ValidSiteURLSchemes { 46 if scheme == u.Scheme { 47 return true 48 } 49 } 50 return false 51 } 52 53 // IsEmailDomainListed checks whether the domain of an email address 54 // matches a list of domains 55 func IsEmailDomainListed(globs []glob.Glob, email string) bool { 56 if len(globs) == 0 { 57 return false 58 } 59 60 n := strings.LastIndex(email, "@") 61 if n <= 0 { 62 return false 63 } 64 65 domain := strings.ToLower(email[n+1:]) 66 67 for _, g := range globs { 68 if g.Match(domain) { 69 return true 70 } 71 } 72 73 return false 74 } 75 76 // IsAPIURL checks if URL is current Gitea instance API URL 77 func IsAPIURL(uri string) bool { 78 return strings.HasPrefix(strings.ToLower(uri), strings.ToLower(setting.AppURL+"api")) 79 } 80 81 // IsValidExternalURL checks if URL is valid external URL 82 func IsValidExternalURL(uri string) bool { 83 if !IsValidURL(uri) || IsAPIURL(uri) { 84 return false 85 } 86 87 u, err := url.ParseRequestURI(uri) 88 if err != nil { 89 return false 90 } 91 92 // Currently check only if not loopback IP is provided to keep compatibility 93 if isLoopbackIP(u.Hostname()) || strings.ToLower(u.Hostname()) == "localhost" { 94 return false 95 } 96 97 // TODO: Later it should be added to allow local network IP addresses 98 // only if allowed by special setting 99 100 return true 101 } 102 103 // IsValidExternalTrackerURLFormat checks if URL matches required syntax for external trackers 104 func IsValidExternalTrackerURLFormat(uri string) bool { 105 if !IsValidExternalURL(uri) { 106 return false 107 } 108 109 // check for typoed variables like /{index/ or /[repo} 110 for _, match := range externalTrackerRegex.FindAllStringSubmatch(uri, -1) { 111 if (match[1] == "{" || match[2] == "}") && (match[1] != "{" || match[2] != "}") { 112 return false 113 } 114 } 115 116 return true 117 } 118 119 var ( 120 validUsernamePattern = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`) 121 invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) // No consecutive or trailing non-alphanumeric chars 122 ) 123 124 // IsValidUsername checks if username is valid 125 func IsValidUsername(name string) bool { 126 // It is difficult to find a single pattern that is both readable and effective, 127 // but it's easier to use positive and negative checks. 128 return validUsernamePattern.MatchString(name) && !invalidUsernamePattern.MatchString(name) 129 }