gitea源码

helpers.go 3.3KB

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